我对着色器很陌生,我一直在尝试在着色玩具中使用它们。我正试图对图形管道有更深入的了解,我想用着色器实现一些功能。我想从简单的开始,用着色器在屏幕上画一条线。我从头开始写了两行算法
struct Vertex {
vec2 p;
vec4 c;
};
vec4 overlay(vec4 c1, vec4 c2) {
return vec4((1.0 - c2.w) * c1.xyz + c2.w * c2.xyz, 1.0);
}
vec4 drawLineA(Vertex v1, Vertex v2, vec2 pos) {
vec2 a = v1.p;
vec2 b = v2.p;
vec2 r = floor(pos);
vec2 diff = b - a;
if (abs(diff.y) < abs(diff.x)) {
if (diff.x < 0.0) {
Vertex temp1 = v1;
Vertex temp2 = v2;
v1 = temp2;
v2 = temp1;
a = v1.p;
b = v2.p;
diff = b - a;
}
float m = diff.y / diff.x;
float q = r.x - a.x;
if (floor(m * q + a.y) == r.y && a.x <= r.x && r.x <= b.x) {
float h = q / diff.x;
return vec4((1.0 - h) * v1.c + h * v2.c);
}
} else {
if (diff.y < 0.0) {
Vertex temp1 = v1;
Vertex temp2 = v2;
v1 = temp2;
v2 = temp1;
a = v1.p;
b = v2.p;
diff = b - a;
}
float m = diff.x / diff.y;
float q = r.y - a.y;
if (floor(m * q + a.x) == r.x && a.y <= r.y && r.y <= b.y) {
float h = q / diff.y;
return vec4((1.0 - h) * v1.c + h * v2.c);
}
}
return vec4(0,0,0,0);
}
vec4 drawLineB(Vertex v1, Vertex v2, vec2 pos) {
vec2 a = v1.p;
vec2 b = v2.p;
vec2 l = b - a;
vec2 r = pos - a;
float h = dot(l,r) / dot (l,l);
vec2 eC = a + h * l;
if (floor(pos) == floor(eC) && 0.0 <= h && h <= 1.0 ) {
return vec4((1.0 - h) * v1.c + h * v2.c);
}
return vec4(0,0,0,0);
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
float t = iTime;
float r = 300.0;
Vertex v1 = Vertex(vec2(400,225), vec4(1,0,0,1));
Vertex v2 = Vertex(vec2(400.0 + r*cos(t) ,225.0 + r*sin(t)), vec4(0,1,0,1));
vec4 col = vec4(0,0,0,1);
col = overlay(col,drawLineA(v1, v2, fragCoord));
col = overlay(col,drawLineB(v1, v2, fragCoord));
// Output to screen
fragColor = col;
}但是,它们都很慢,是否有一个更有效的线算法,我可以用它来根据像素是否在线上输出颜色。我听说Bresenham的直线算法是最快的,但是我不知道如何使用着色器来加速算法,因为它需要前面的y值来计算下一个。行算法可以通过着色器加速,或者它已经是CPU上最快的了。如果有一个使用GPU ( GPU将优于cpu)的优化绘制算法,我如何将其实现到代码中?
谢谢
发布于 2021-02-21 01:07:18
以下是SDF的一个(主要是)最小版本,用于使用带符号距离字段的线段,如注释中所建议的那样。我不相信这个代码,它是您的功能拼凑在一起的IQ的SDF功能所做的风格,你上面列出的。
一定要去IQ的网站,这是最好的学习资源之一,为这方面的材料。不过,除非你把所有的基本知识都弄下来,否则就很难消化。智商指数页。
float udSegment( in vec2 p, in vec2 a, in vec2 b )
{
vec2 ba = b-a;
vec2 pa = p-a;
float h =clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
//return length(pa-h*ba); // use actual length
// or use length squared
vec2 sqrd = pa-h*ba;
sqrd = sqrd * sqrd;
return sqrd.x+sqrd.y;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 p = ((2.0*fragCoord-iResolution.xy)/iResolution.y)*1.4;
// give it some spin
vec2 v1 = iResolution.xy * vec2(cos(iTime),sin(iTime));
vec2 v2 = vec2(0,0); // set the start point
float th = 0.004; // Controls line thickness
float d = udSegment( p, v1, v2 ) - th; // compute sdf
vec3 col = vec3(1.0) - sign(d);
fragColor = vec4(col,1.0);
}如果所有这些都是画一条线,那么就不需要计算实际长度,所以不要.只返回长平方。我对上面跳过sqrt的代码进行了编辑,但是行厚现在是平方的。
Bresenham算法是用来绘制单个像素的,通常直接在GPU的硬件中实现,因此它驱动在自己的循环中绘制哪些像素,并告诉硬件要绘制哪个像素。
ShaderToy正在绘制整个屏幕,因此每个像素都可能通过绘制全屏三角形来覆盖,然后在碎片着色器中调用mainImage函数。
在GPU上绘制线的最有效的方法是根本不使用ShaderToy,而是编写我们自己的代码,告诉硬件只画一条线,这就是图形API(如OpenGL )发挥作用的地方。它最终会调用片段着色器,但与ShaderToy不同的是,片段着色器只会被调用到那些实际处于线上的片段,然后我们的片段着色器将被简化为一些非常简单的东西,比如:
void main(){ return vec4(1,1,1,1); }这将导致在屏幕上画一条纯白线。
https://computergraphics.stackexchange.com/questions/10682
复制相似问题