我有一个简单的片段着色器,可以绘制测试网格图案。
我确实没有问题-但我注意到我无法解释的怪异行为。不要介意怪异的常量-在编译之前,在着色器组装过程中会填充它们。另外,vertexPosition
是世界空间中的实际计算位置,因此我可以在网格物体自身移动时移动着色器纹理。
这是我的着色器的代码:
#version 300 es
precision highp float;
in highp vec3 vertexPosition;
out mediump vec4 fragColor;
const float squareSize = __CONSTANT_SQUARE_SIZE;
const vec3 color_base = __CONSTANT_COLOR_BASE;
const vec3 color_l1 = __CONSTANT_COLOR_L1;
float minWidthX;
float minWidthY;
vec3 color_green = vec3(0.0,1.0,0.0);
void main()
{
// calculate l1 border positions
float dimention = squareSize;
int roundX = int(vertexPosition.x / dimention);
int roundY = int(vertexPosition.z / dimention);
float remainderX = vertexPosition.x - float(roundX)*dimention;
float remainderY = vertexPosition.z - float(roundY)*dimention;
vec3 dyX = dFdy(vec3(vertexPosition.x, vertexPosition.y, 0));
vec3 dxX = dFdx(vec3(vertexPosition.x, vertexPosition.y, 0));
minWidthX = max(length(dxX),length(dyX));
vec3 dyY = dFdy(vec3(0, vertexPosition.y, vertexPosition.z));
vec3 dxY = dFdx(vec3(0, vertexPosition.y, vertexPosition.z));
minWidthY = max(length(dxY),length(dyY));
//Fill l1 suqares
if (remainderX <= minWidthX)
{
fragColor = vec4(color_l1, 1.0);
return;
}
if (remainderY <= minWidthY)
{
fragColor = vec4(color_l1, 1.0);
return;
}
// fill base color
fragColor = vec4(color_base, 1.0);
return;
}
因此,使用此代码,一切正常。然后,我想通过移动只绘制水平线之后只涉及水平线的计算来对其进行一点优化。因为如果垂直线检查为true,则这些计算无用。像这样:
#version 300 es
precision highp float;
in highp vec3 vertexPosition;
out mediump vec4 fragColor;
const float squareSize = __CONSTANT_SQUARE_SIZE;
const vec3 color_base = __CONSTANT_COLOR_BASE;
const vec3 color_l1 = __CONSTANT_COLOR_L1;
float minWidthX;
float minWidthY;
vec3 color_green = vec3(0.0,1.0,0.0);
void main()
{
// calculate l1 border positions
float dimention = squareSize;
int roundX = int(vertexPosition.x / dimention);
int roundY = int(vertexPosition.z / dimention);
float remainderX = vertexPosition.x - float(roundX)*dimention;
float remainderY = vertexPosition.z - float(roundY)*dimention;
vec3 dyX = dFdy(vec3(vertexPosition.x, vertexPosition.y, 0));
vec3 dxX = dFdx(vec3(vertexPosition.x, vertexPosition.y, 0));
minWidthX = max(length(dxX),length(dyX));
//Fill l1 suqares
if (remainderX <= minWidthX)
{
fragColor = vec4(color_l1, 1.0);
return;
}
vec3 dyY = dFdy(vec3(0, vertexPosition.y, vertexPosition.z));
vec3 dxY = dFdx(vec3(0, vertexPosition.y, vertexPosition.z));
minWidthY = max(length(dxY),length(dyY));
if (remainderY <= minWidthY)
{
fragColor = vec4(color_l1, 1.0);
return;
}
// fill base color
fragColor = vec4(color_base, 1.0);
return;
}
但是,即使表面上这似乎不应该影响结果,但确实如此。相当多。以下是两个屏幕截图。第一个是原始代码,第二个是“优化”代码。哪个效果不好。
原始版本:
优化版本(看起来更糟):
请注意,尽管看似没有任何数字,但这些行如何变得“模糊”。
注意:这不是因为minwidthX / Y是全局的。我最初通过将它们设置为本地进行了优化。我最初也将RoundY和restderY计算也移到了X支票下,结果是相同的。
注2:我尝试为每个计算专门添加highp关键字,但这并没有改变任何内容(不是我期望的那样,但是我还是尝试了)
有人可以向我解释为什么会这样吗?我想知道我的未来着色器,实际上我也想对其进行优化。我想在这里了解精度损失背后的原理,因为它对我没有任何意义。
关于答案,我将参考OpenGL ES Shading Language 3.20 Specification,在这一点上它与OpenGL ES Shading Language 3.00 Specification相同。
8.14.1。微分函数
[...]导数在非均匀控制流中未定义。
以及更多
3.9.2。统一和非统一的控制流程
[在片段着色器中执行语句时,控制流以统一的控制流开始;所有片段都在main()中输入相同的控制路径。当不同的片段通过控制流语句(选择,迭代和跳转)采取不同的路径时,控制流将变得不一致。[...]
这意味着,(在您的问题中)第一种情况的导数函数的结果得到了很好的定义。
但是在第二种情况下不是:
if (remainderX <= minWidthX) { fragColor = vec4(color_l1, 1.0); return; } vec3 dyY = dFdy(vec3(0, vertexPosition.y, vertexPosition.z)); vec3 dxY = dFdx(vec3(0, vertexPosition.y, vertexPosition.z));
因为return
语句的作用类似于选择。并且带有return语句的代码块之后的所有代码都处于不一致的控制流中。