Merge "Use LUT for computing final shadow alpha" into nyc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
b2e36d7939
@@ -43,9 +43,7 @@
|
||||
/**
|
||||
* Other constants:
|
||||
*/
|
||||
// For the edge of the penumbra, the opacity is 0. After transform (1 - alpha),
|
||||
// it is 1.
|
||||
#define TRANSFORMED_OUTER_OPACITY (1.0f)
|
||||
#define OUTER_ALPHA (0.0f)
|
||||
|
||||
// Once the alpha difference is greater than this threshold, we will allocate extra
|
||||
// edge vertices.
|
||||
@@ -81,17 +79,6 @@ inline float getAlphaFromFactoredZ(float factoredZ) {
|
||||
return 1.0 / (1 + std::max(factoredZ, 0.0f));
|
||||
}
|
||||
|
||||
// The shader is using gaussian function e^-(1-x)*(1-x)*4, therefore, we transform
|
||||
// the alpha value to (1 - alpha)
|
||||
inline float getTransformedAlphaFromAlpha(float alpha) {
|
||||
return 1.0f - alpha;
|
||||
}
|
||||
|
||||
// The output is ranged from 0 to 1.
|
||||
inline float getTransformedAlphaFromFactoredZ(float factoredZ) {
|
||||
return getTransformedAlphaFromAlpha(getAlphaFromFactoredZ(factoredZ));
|
||||
}
|
||||
|
||||
inline int getEdgeExtraAndUpdateSpike(Vector2* currentSpike,
|
||||
const Vector3& secondVertex, const Vector3& centroid) {
|
||||
Vector2 secondSpike = {secondVertex.x - centroid.x, secondVertex.y - centroid.y};
|
||||
@@ -225,9 +212,9 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque,
|
||||
if (!isCasterOpaque) {
|
||||
umbraVertices[umbraIndex++] = vertexBufferIndex;
|
||||
}
|
||||
AlphaVertex::set(&shadowVertices[vertexBufferIndex++], casterVertices[i].x,
|
||||
casterVertices[i].y,
|
||||
getTransformedAlphaFromAlpha(currentAlpha));
|
||||
AlphaVertex::set(&shadowVertices[vertexBufferIndex++],
|
||||
casterVertices[i].x, casterVertices[i].y,
|
||||
currentAlpha);
|
||||
|
||||
const Vector3& innerStart = casterVertices[i];
|
||||
|
||||
@@ -249,7 +236,7 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque,
|
||||
indexBuffer[indexBufferIndex++] = vertexBufferIndex;
|
||||
indexBuffer[indexBufferIndex++] = currentInnerVertexIndex;
|
||||
AlphaVertex::set(&shadowVertices[vertexBufferIndex++], outerVertex.x,
|
||||
outerVertex.y, TRANSFORMED_OUTER_OPACITY);
|
||||
outerVertex.y, OUTER_ALPHA);
|
||||
|
||||
if (j == 0) {
|
||||
outerStart = outerVertex;
|
||||
@@ -285,7 +272,7 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque,
|
||||
(outerLast * startWeight + outerNext * k) / extraVerticesNumber;
|
||||
indexBuffer[indexBufferIndex++] = vertexBufferIndex;
|
||||
AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentOuter.x,
|
||||
currentOuter.y, TRANSFORMED_OUTER_OPACITY);
|
||||
currentOuter.y, OUTER_ALPHA);
|
||||
|
||||
if (!isCasterOpaque) {
|
||||
umbraVertices[umbraIndex++] = vertexBufferIndex;
|
||||
@@ -295,7 +282,7 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque,
|
||||
indexBuffer[indexBufferIndex++] = vertexBufferIndex;
|
||||
AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentInner.x,
|
||||
currentInner.y,
|
||||
getTransformedAlphaFromFactoredZ(currentInner.z * heightFactor));
|
||||
getAlphaFromFactoredZ(currentInner.z * heightFactor));
|
||||
}
|
||||
}
|
||||
currentAlpha = nextAlpha;
|
||||
@@ -307,7 +294,7 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque,
|
||||
if (!isCasterOpaque) {
|
||||
// Add the centroid as the last one in the vertex buffer.
|
||||
float centroidOpacity =
|
||||
getTransformedAlphaFromFactoredZ(centroid3d.z * heightFactor);
|
||||
getAlphaFromFactoredZ(centroid3d.z * heightFactor);
|
||||
int centroidIndex = vertexBufferIndex;
|
||||
AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid3d.x,
|
||||
centroid3d.y, centroidOpacity);
|
||||
|
||||
@@ -346,11 +346,12 @@ static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& st
|
||||
bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
|
||||
const int transformFlags = vertexBufferRenderFlags & VertexBufferRenderFlags::Offset
|
||||
? TransformFlags::OffsetByFudgeFactor : 0;
|
||||
|
||||
Glop glop;
|
||||
GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
|
||||
.setRoundRectClipState(state.roundRectClipState)
|
||||
.setMeshVertexBuffer(vertexBuffer, shadowInterp)
|
||||
.setFillPaint(paint, state.alpha)
|
||||
.setMeshVertexBuffer(vertexBuffer)
|
||||
.setFillPaint(paint, state.alpha, shadowInterp)
|
||||
.setTransform(state.computedState.transform, transformFlags)
|
||||
.setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
|
||||
.build();
|
||||
|
||||
@@ -74,6 +74,7 @@ bool Caches::init() {
|
||||
|
||||
mPixelBufferState = new PixelBufferState();
|
||||
mTextureState = new TextureState();
|
||||
mTextureState->constructTexture(*this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -44,9 +44,14 @@ class Texture;
|
||||
|
||||
namespace VertexAttribFlags {
|
||||
enum {
|
||||
// Mesh is pure x,y vertex pairs
|
||||
None = 0,
|
||||
// Mesh has texture coordinates embedded. Note that texture can exist without this flag
|
||||
// being set, if coordinates passed to sampler are determined another way.
|
||||
TextureCoord = 1 << 0,
|
||||
// Mesh has color embedded (to export to varying)
|
||||
Color = 1 << 1,
|
||||
// Mesh has alpha embedded (to export to varying)
|
||||
Alpha = 1 << 2,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -193,7 +193,7 @@ GlopBuilder& GlopBuilder::setMeshColoredTexturedMesh(ColorTextureVertex* vertexD
|
||||
return *this;
|
||||
}
|
||||
|
||||
GlopBuilder& GlopBuilder::setMeshVertexBuffer(const VertexBuffer& vertexBuffer, bool shadowInterp) {
|
||||
GlopBuilder& GlopBuilder::setMeshVertexBuffer(const VertexBuffer& vertexBuffer) {
|
||||
TRIGGER_STAGE(kMeshStage);
|
||||
|
||||
const VertexBuffer::MeshFeatureFlags flags = vertexBuffer.getMeshFeatureFlags();
|
||||
@@ -210,8 +210,6 @@ GlopBuilder& GlopBuilder::setMeshVertexBuffer(const VertexBuffer& vertexBuffer,
|
||||
alphaVertex ? kAlphaVertexStride : kVertexStride };
|
||||
mOutGlop->mesh.elementCount = indices
|
||||
? vertexBuffer.getIndexCount() : vertexBuffer.getVertexCount();
|
||||
|
||||
mDescription.useShadowAlphaInterp = shadowInterp;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -368,15 +366,23 @@ GlopBuilder& GlopBuilder::setFillTexturePaint(Texture& texture,
|
||||
return *this;
|
||||
}
|
||||
|
||||
GlopBuilder& GlopBuilder::setFillPaint(const SkPaint& paint, float alphaScale) {
|
||||
GlopBuilder& GlopBuilder::setFillPaint(const SkPaint& paint, float alphaScale, bool shadowInterp) {
|
||||
TRIGGER_STAGE(kFillStage);
|
||||
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
|
||||
|
||||
mOutGlop->fill.texture = { nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
|
||||
if (CC_LIKELY(!shadowInterp)) {
|
||||
mOutGlop->fill.texture = {
|
||||
nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
|
||||
} else {
|
||||
mOutGlop->fill.texture = {
|
||||
mCaches.textureState().getShadowLutTexture(), GL_TEXTURE_2D,
|
||||
GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr };
|
||||
}
|
||||
|
||||
setFill(paint.getColor(), alphaScale,
|
||||
PaintUtils::getXfermode(paint.getXfermode()), Blend::ModeOrderSwap::NoSwap,
|
||||
paint.getShader(), paint.getColorFilter());
|
||||
mDescription.useShadowAlphaInterp = shadowInterp;
|
||||
mDescription.modulate = mOutGlop->fill.color.a < 1.0f;
|
||||
return *this;
|
||||
}
|
||||
@@ -593,8 +599,11 @@ GlopBuilder& GlopBuilder::setRoundRectClipState(const RoundRectClipState* roundR
|
||||
void verify(const ProgramDescription& description, const Glop& glop) {
|
||||
if (glop.fill.texture.texture != nullptr) {
|
||||
LOG_ALWAYS_FATAL_IF(((description.hasTexture && description.hasExternalTexture)
|
||||
|| (!description.hasTexture && !description.hasExternalTexture)
|
||||
|| ((glop.mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) == 0)),
|
||||
|| (!description.hasTexture
|
||||
&& !description.hasExternalTexture
|
||||
&& !description.useShadowAlphaInterp)
|
||||
|| ((glop.mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) == 0
|
||||
&& !description.useShadowAlphaInterp)),
|
||||
"Texture %p, hT%d, hET %d, attribFlags %x",
|
||||
glop.fill.texture.texture,
|
||||
description.hasTexture, description.hasExternalTexture,
|
||||
|
||||
@@ -51,14 +51,14 @@ public:
|
||||
GlopBuilder& setMeshUnitQuad();
|
||||
GlopBuilder& setMeshTexturedUnitQuad(const UvMapper* uvMapper);
|
||||
GlopBuilder& setMeshTexturedUvQuad(const UvMapper* uvMapper, const Rect uvs);
|
||||
GlopBuilder& setMeshVertexBuffer(const VertexBuffer& vertexBuffer, bool shadowInterp);
|
||||
GlopBuilder& setMeshVertexBuffer(const VertexBuffer& vertexBuffer);
|
||||
GlopBuilder& setMeshIndexedQuads(Vertex* vertexData, int quadCount);
|
||||
GlopBuilder& setMeshTexturedMesh(TextureVertex* vertexData, int elementCount); // TODO: delete
|
||||
GlopBuilder& setMeshColoredTexturedMesh(ColorTextureVertex* vertexData, int elementCount); // TODO: use indexed quads
|
||||
GlopBuilder& setMeshTexturedIndexedQuads(TextureVertex* vertexData, int elementCount); // TODO: take quadCount
|
||||
GlopBuilder& setMeshPatchQuads(const Patch& patch);
|
||||
|
||||
GlopBuilder& setFillPaint(const SkPaint& paint, float alphaScale);
|
||||
GlopBuilder& setFillPaint(const SkPaint& paint, float alphaScale, bool shadowInterp = false); // TODO: avoid boolean with default
|
||||
GlopBuilder& setFillTexturePaint(Texture& texture, const int textureFillFlags,
|
||||
const SkPaint* paint, float alphaScale);
|
||||
GlopBuilder& setFillPathTexturePaint(PathTexture& texture,
|
||||
|
||||
@@ -1697,8 +1697,8 @@ void OpenGLRenderer::drawVertexBuffer(float translateX, float translateY,
|
||||
Glop glop;
|
||||
GlopBuilder(mRenderState, mCaches, &glop)
|
||||
.setRoundRectClipState(currentSnapshot()->roundRectClipState)
|
||||
.setMeshVertexBuffer(vertexBuffer, shadowInterp)
|
||||
.setFillPaint(*paint, currentSnapshot()->alpha)
|
||||
.setMeshVertexBuffer(vertexBuffer)
|
||||
.setFillPaint(*paint, currentSnapshot()->alpha, shadowInterp)
|
||||
.setTransform(*currentSnapshot(), transformFlags)
|
||||
.setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
|
||||
.build();
|
||||
|
||||
@@ -231,9 +231,8 @@ const char* gFS_Main_ModulateColor =
|
||||
const char* gFS_Main_ApplyVertexAlphaLinearInterp =
|
||||
" fragColor *= alpha;\n";
|
||||
const char* gFS_Main_ApplyVertexAlphaShadowInterp =
|
||||
// Use a gaussian function for the shadow fall off. Note that alpha here
|
||||
// is actually (1.0 - alpha) for saving computation.
|
||||
" fragColor *= exp(- alpha * alpha * 4.0) - 0.018;\n";
|
||||
// map alpha through shadow alpha sampler
|
||||
" fragColor *= texture2D(baseSampler, vec2(alpha, 0.5)).a;\n";
|
||||
const char* gFS_Main_FetchTexture[2] = {
|
||||
// Don't modulate
|
||||
" fragColor = texture2D(baseSampler, outTexCoords);\n",
|
||||
@@ -565,7 +564,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
|
||||
shader.append(gFS_Uniforms_Color);
|
||||
if (!singleColor) modulateOp = MODULATE_OP_MODULATE;
|
||||
}
|
||||
if (description.hasTexture) {
|
||||
if (description.hasTexture || description.useShadowAlphaInterp) {
|
||||
shader.append(gFS_Uniforms_TextureSampler);
|
||||
} else if (description.hasExternalTexture) {
|
||||
shader.append(gFS_Uniforms_ExternalTextureSampler);
|
||||
|
||||
@@ -42,9 +42,8 @@
|
||||
// For each RADIANS_DIVISOR, we would allocate one more vertex b/t the normals.
|
||||
#define SPOT_CORNER_RADIANS_DIVISOR (M_PI / SPOT_EXTRA_CORNER_VERTEX_PER_PI)
|
||||
|
||||
// For performance, we use (1 - alpha) value for the shader input.
|
||||
#define TRANSFORMED_PENUMBRA_ALPHA 1.0f
|
||||
#define TRANSFORMED_UMBRA_ALPHA 0.0f
|
||||
#define PENUMBRA_ALPHA 0.0f
|
||||
#define UMBRA_ALPHA 1.0f
|
||||
|
||||
#include "SpotShadow.h"
|
||||
|
||||
@@ -941,11 +940,11 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength
|
||||
// Fill the IB and VB for the penumbra area.
|
||||
for (int i = 0; i < newPenumbraLength; i++) {
|
||||
AlphaVertex::set(&shadowVertices[vertexBufferIndex++], newPenumbra[i].x,
|
||||
newPenumbra[i].y, TRANSFORMED_PENUMBRA_ALPHA);
|
||||
newPenumbra[i].y, PENUMBRA_ALPHA);
|
||||
}
|
||||
for (int i = 0; i < umbraLength; i++) {
|
||||
AlphaVertex::set(&shadowVertices[vertexBufferIndex++], umbra[i].x, umbra[i].y,
|
||||
TRANSFORMED_UMBRA_ALPHA);
|
||||
UMBRA_ALPHA);
|
||||
}
|
||||
|
||||
for (int i = 0; i < verticesPairIndex; i++) {
|
||||
@@ -985,14 +984,14 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength
|
||||
indexBuffer[indexBufferIndex++] = newPenumbraLength + i;
|
||||
indexBuffer[indexBufferIndex++] = vertexBufferIndex;
|
||||
AlphaVertex::set(&shadowVertices[vertexBufferIndex++],
|
||||
closerVertex.x, closerVertex.y, TRANSFORMED_UMBRA_ALPHA);
|
||||
closerVertex.x, closerVertex.y, UMBRA_ALPHA);
|
||||
}
|
||||
} else {
|
||||
// If there is no occluded umbra at all, then draw the triangle fan
|
||||
// starting from the centroid to all umbra vertices.
|
||||
int lastCentroidIndex = vertexBufferIndex;
|
||||
AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid.x,
|
||||
centroid.y, TRANSFORMED_UMBRA_ALPHA);
|
||||
centroid.y, UMBRA_ALPHA);
|
||||
for (int i = 0; i < umbraLength; i++) {
|
||||
indexBuffer[indexBufferIndex++] = newPenumbraLength + i;
|
||||
indexBuffer[indexBufferIndex++] = lastCentroidIndex;
|
||||
|
||||
@@ -298,7 +298,8 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) {
|
||||
// indices
|
||||
meshState().bindIndicesBuffer(indices.bufferObject);
|
||||
|
||||
if (vertices.attribFlags & VertexAttribFlags::TextureCoord) {
|
||||
// texture
|
||||
if (fill.texture.texture != nullptr) {
|
||||
const Glop::Fill::TextureData& texture = fill.texture;
|
||||
// texture always takes slot 0, shader samplers increment from there
|
||||
mCaches->textureState().activateTexture(0);
|
||||
@@ -311,13 +312,16 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) {
|
||||
texture.texture->setFilter(texture.filter, false, false, texture.target);
|
||||
}
|
||||
|
||||
meshState().enableTexCoordsVertexArray();
|
||||
meshState().bindTexCoordsVertexPointer(vertices.texCoord, vertices.stride);
|
||||
|
||||
if (texture.textureTransform) {
|
||||
glUniformMatrix4fv(fill.program->getUniform("mainTextureTransform"), 1,
|
||||
GL_FALSE, &texture.textureTransform->data[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// vertex attributes (tex coord, color, alpha)
|
||||
if (vertices.attribFlags & VertexAttribFlags::TextureCoord) {
|
||||
meshState().enableTexCoordsVertexArray();
|
||||
meshState().bindTexCoordsVertexPointer(vertices.texCoord, vertices.stride);
|
||||
} else {
|
||||
meshState().disableTexCoordsVertexArray();
|
||||
}
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
// Width of mShadowLutTexture, defines how accurate the shadow alpha lookup table is
|
||||
static const int SHADOW_LUT_SIZE = 128;
|
||||
|
||||
// Must define as many texture units as specified by kTextureUnitsCount
|
||||
const GLenum kTextureUnits[] = {
|
||||
GL_TEXTURE0,
|
||||
@@ -46,6 +49,41 @@ TextureState::TextureState()
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
}
|
||||
|
||||
TextureState::~TextureState() {
|
||||
if (mShadowLutTexture != nullptr) {
|
||||
mShadowLutTexture->deleteTexture();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps shadow geometry 'alpha' varying (1 for darkest, 0 for transparent) to
|
||||
* darkness at that spot. Input values of 0->1 should be mapped within the same
|
||||
* range, but can affect the curve for a different visual falloff.
|
||||
*
|
||||
* This is used to populate the shadow LUT texture for quick lookup in the
|
||||
* shadow shader.
|
||||
*/
|
||||
static float computeShadowOpacity(float ratio) {
|
||||
// exponential falloff function provided by UX
|
||||
float val = 1 - ratio;
|
||||
return exp(-val * val * 4.0) - 0.018;
|
||||
}
|
||||
|
||||
void TextureState::constructTexture(Caches& caches) {
|
||||
if (mShadowLutTexture == nullptr) {
|
||||
mShadowLutTexture.reset(new Texture(caches));
|
||||
|
||||
unsigned char bytes[SHADOW_LUT_SIZE];
|
||||
for (int i = 0; i < SHADOW_LUT_SIZE; i++) {
|
||||
float inputRatio = i / (SHADOW_LUT_SIZE - 1.0f);
|
||||
bytes[i] = computeShadowOpacity(inputRatio) * 255;
|
||||
}
|
||||
mShadowLutTexture->upload(GL_ALPHA, SHADOW_LUT_SIZE, 1, GL_ALPHA, GL_UNSIGNED_BYTE, &bytes);
|
||||
mShadowLutTexture->setFilter(GL_LINEAR);
|
||||
mShadowLutTexture->setWrap(GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
}
|
||||
|
||||
void TextureState::activateTexture(GLuint textureUnit) {
|
||||
LOG_ALWAYS_FATAL_IF(textureUnit >= kTextureUnitsCount,
|
||||
"Tried to use texture unit index %d, only %d exist",
|
||||
|
||||
@@ -17,14 +17,12 @@
|
||||
#define RENDERSTATE_TEXTURESTATE_H
|
||||
|
||||
#include "Vertex.h"
|
||||
#include "Texture.h"
|
||||
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#include <SkXfermode.h>
|
||||
#include <memory>
|
||||
|
||||
class SkBitmap;
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
@@ -33,6 +31,9 @@ class Texture;
|
||||
class TextureState {
|
||||
friend class Caches; // TODO: move to RenderState
|
||||
public:
|
||||
|
||||
void constructTexture(Caches& caches);
|
||||
|
||||
/**
|
||||
* Activate the specified texture unit. The texture unit must
|
||||
* be specified using an integer number (0 for GL_TEXTURE0 etc.)
|
||||
@@ -76,15 +77,20 @@ public:
|
||||
*/
|
||||
void unbindTexture(GLuint texture);
|
||||
|
||||
Texture* getShadowLutTexture() { return mShadowLutTexture.get(); }
|
||||
|
||||
private:
|
||||
// total number of texture units available for use
|
||||
static const int kTextureUnitsCount = 4;
|
||||
|
||||
TextureState();
|
||||
~TextureState();
|
||||
GLuint mTextureUnit;
|
||||
|
||||
// Caches texture bindings for the GL_TEXTURE_2D target
|
||||
GLuint mBoundTextures[kTextureUnitsCount];
|
||||
|
||||
std::unique_ptr<Texture> mShadowLutTexture;
|
||||
};
|
||||
|
||||
} /* namespace uirenderer */
|
||||
|
||||
Reference in New Issue
Block a user