GLSL Gif 抖动效果:优化
我有一个片段着色器,它本质上读取颜色 alpha 并将其转换为跨着色器的抖动效果像素。
然而,所有 mod 和 if 语句都非常消耗处理器资源。 有人对优化下面的代码有什么建议吗?
varying vec2 the_uv;
varying vec4 color;
void main()
{
// The pixel color will correspond
// to the uv coords of the texture
// for the given vertice, retrieved
// by the Vertex shader through varying vec2 the_uv
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
vec4 tex = texture2D(_MainTex, the_uv);
tex = tex * color ;
float r = tex.a;
if ( r > 0.1 ) {
if ( ( mod(gl_FragCoord.x, 4.001) + mod(gl_FragCoord.y, 4.0) ) > 6.00 ) {
gl_FragColor = color;
}
}
if ( r > 0.5 ) {
if ( ( mod(gl_FragCoord.x + 2.0, 4.001) + mod(gl_FragCoord.y, 4.0) ) > 6.00 ) {
gl_FragColor = color;
}
}
if ( r > 0.7 ) {
if ( ( mod(gl_FragCoord.x, 4.001) + mod(gl_FragCoord.y + 2.0, 4.0) ) > 6.00 ) {
gl_FragColor = color;
}
}
if ( r > 0.9 ) {
if ( ( mod(gl_FragCoord.x + 1.0, 4.001) + mod(gl_FragCoord.y + 1.0, 4.0) ) > 6.00 ) {
gl_FragColor = color;
}
}
if ( r > 0.3 ) {
if ( ( mod(gl_FragCoord.x + 2.0, 4.001) + mod(gl_FragCoord.y + 2.0, 4.0) ) > 6.00 ) {
gl_FragColor = color;
}
}
}
以下是根据反馈的解决方案:
varying vec2 the_uv;
varying vec4 color;
void main()
{
color = gl_Color;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
the_uv = gl_MultiTexCoord0.st;
}
#endif
#ifdef FRAGMENT
uniform sampler2D _MainTex;
uniform sampler2D _GridTex;
varying vec2 the_uv;
varying vec4 color;
void main()
{
if (texture2D(_MainTex, the_uv).a * color.a > texture2D(_GridTex, vec2(gl_FragCoord.x, gl_FragCoord.y)*.25).a) gl_FragColor = color;
else gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
}
I've got a fragment shader that essentially reads the color alpha and translates it into a dithering effect across the pixels.
However, it's quite processor intensive with all the mods and if statements.
Does anyone have any recommendations on optimizing the code below?
varying vec2 the_uv;
varying vec4 color;
void main()
{
// The pixel color will correspond
// to the uv coords of the texture
// for the given vertice, retrieved
// by the Vertex shader through varying vec2 the_uv
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
vec4 tex = texture2D(_MainTex, the_uv);
tex = tex * color ;
float r = tex.a;
if ( r > 0.1 ) {
if ( ( mod(gl_FragCoord.x, 4.001) + mod(gl_FragCoord.y, 4.0) ) > 6.00 ) {
gl_FragColor = color;
}
}
if ( r > 0.5 ) {
if ( ( mod(gl_FragCoord.x + 2.0, 4.001) + mod(gl_FragCoord.y, 4.0) ) > 6.00 ) {
gl_FragColor = color;
}
}
if ( r > 0.7 ) {
if ( ( mod(gl_FragCoord.x, 4.001) + mod(gl_FragCoord.y + 2.0, 4.0) ) > 6.00 ) {
gl_FragColor = color;
}
}
if ( r > 0.9 ) {
if ( ( mod(gl_FragCoord.x + 1.0, 4.001) + mod(gl_FragCoord.y + 1.0, 4.0) ) > 6.00 ) {
gl_FragColor = color;
}
}
if ( r > 0.3 ) {
if ( ( mod(gl_FragCoord.x + 2.0, 4.001) + mod(gl_FragCoord.y + 2.0, 4.0) ) > 6.00 ) {
gl_FragColor = color;
}
}
}
Here is the solution based on the feedback:
varying vec2 the_uv;
varying vec4 color;
void main()
{
color = gl_Color;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
the_uv = gl_MultiTexCoord0.st;
}
#endif
#ifdef FRAGMENT
uniform sampler2D _MainTex;
uniform sampler2D _GridTex;
varying vec2 the_uv;
varying vec4 color;
void main()
{
if (texture2D(_MainTex, the_uv).a * color.a > texture2D(_GridTex, vec2(gl_FragCoord.x, gl_FragCoord.y)*.25).a) gl_FragColor = color;
else gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您要做的是根据源 Alpha 选择是否应在 4x4 网格上点亮每个像素。最简单的方法就是这样做。
首先使用像素通道所需的相应 alpha 初始化一个 4x4 纹理(我选择 1.0 作为 alpha,因为此处从未显示)
使用重复设置此纹理以完全避免 mod,并设置最近的过滤器以避免线性过滤。
然后,使用该纹理的采样来决定是否点亮该像素。
这里假设 r 永远不为 1。有一些简单的方法可以解决这个问题,例如,将 4x4 纹理中的值除以 2(1.0 除外),然后在 if 测试之前但在获取之后将抖动乘以 2。
一些进一步的潜在优化:
What you're trying to do is to select whether each pixel should be lit on a 4x4 grid based on the source alpha. The simplest to do that is to do just that.
First initialize a 4x4 texture with the corresponding alphas that are required to have the pixel pass (I picked 1.0 as the alpha for never showing here)
Setup this texture with repeat to avoid the mod completely, and a nearest filter to avoid linear filtering.
Then, use that a sampling of that texture to decide whether to light the pixel.
This assumes that r is never 1. There are simple ways to work around that, e.g. divide the values in the 4x4 texture by 2, except the 1.0's, and then multiply dither by 2 before the if test, but after the fetch.
Some further potential optimizations:
首先,无论 if 语句的结果如何,片段颜色都将是相同的,因为颜色是在开始时设置的。
其次,仅使用 if 语句而不使用 else if 会使您的代码无论如何都可以运行每个计算。
更好的方法是:
当然,这不会帮助您更改输出,因为颜色永远不会改变(正如我之前提到的)。但这应该会让事情运行得更快一些。
另一件需要考虑的事情是哪些案件最常执行。我假设 r 较大的情况更为常见,因此 if 语句的顺序如下。如果较小的 r 更常见,则颠倒顺序并使 r < 0.1,r<0.1 0.3,r<0.3 0.5,r<0.5 0.7,r<0.7 0.9 更有意义。
这里的重点是使代码尽快退出(即让其中一个 if 返回 true)。一旦其中一个 if 返回 true,其余的将被忽略,从而使您无需计算所有其余的 mod 操作。
First of all, your fragment color will be the same regardless of the results of the if statements since color is set at the beginning.
Secondly, using only if statements with no else if's makes your code run through each calculation no matter what.
A much better way to do this would be to have :
Of course this won't help you change the output since color is never changes (as I mentioned previously). But this should make things run a bit faster.
One other thing to take into account is what cases get executed most often. I assumed that the cases with large r are more common, hence the ordering of the if statements. If small r's are more common, then reversing the order and having r < 0.1, r < 0.3, r < 0.5, r < 0.7, r < 0.9 would make more sense.
The whole point here is to make the code exit as fast as possible (i.e. have one of the if's return true). Once one of the if's returns true, the rest are ignored, saving you from calculating all the rest of the mod operations.