如何在 glsl 片段着色器中渲染多个球体

How to render multiple spheres in a glsl fragment shader

我编写了一个片段着色器,它使用简单的光线追踪来渲染片段着色器内部的球体,该片段着色器对形状进行漫反射着色。我可以让它在一个领域工作;但是,当我尝试制作多个球体时,只能看到一个。在着色器的 main() 函数中,我基本上正在执行相同的过程,为我想要制作的每个附加球体渲染一个球体,但这目前似乎不起作用。

附加信息:

球体的顶点 (x,y,z,r) 使用以下方法找到: x 和 y 的顶点位置(即 float x = vPosition.x;float y = vPosition.y;)。 z 的顶点位置是使用下面代码中的 computeZ() 函数计算的。最后,对于 sphere.r,我使用了光线的原点顶点 (V) + 光线的方向 (W)。光线本身从原点射入屏幕,并根据它是否击中球体来确定球体的颜色(我在 raySphere().[=19 中有执行此功能的代码) =]

我想我只需要在 main 中创建一个数组来存储球体,然后创建一个 for 循环来为数组中的每个球体调用 raySphere()shadeSphere() 函数(这是我最初尝试但没有用的方法。

主要是,在初始化一个球体的顶点位置和光线的 V 和 W 之后,我有一个 if 语句来检查 z 是否大于 0,这意味着着色器当前在球体内部,因此它可以继续着色,否则什么也不做。由于以这种方式定义球体的边界,我不确定如何使用我之前提到的 for 循环解决方案将相同的逻辑应用于多个球体。同样在 main 中,我检查 t 值(对于每个球体)是否小于 10000,如果是,则调用 shadeSphere() 函数对球体进行着色;但是,我当前的逻辑也不适用于多个领域。湍流和分形函数只是为了添加程序纹理,它们很长,所以我不会为它们添加代码。

制作多个球体的正确方法是什么?

编辑:

*根据我有一个 if 语句为每个球体设置 gl_FragColor 并且实际渲染的唯一颜色是一个这一事实,球体似乎被渲染在彼此之上在最后一条语句中设置(见代码末尾)

我有下面的相关代码:

<script src=lib1.js></script>

<body bgcolor=black>
<center>
<td><canvas id='canvas1' width=550 height=550></canvas></td>
</center>
</body>

<script id='my_vertex_shader' type='x-shader/x-vertex'>
   attribute vec3 aPosition;
   varying   vec3 vPosition;
   void main() {
      gl_Position = vec4(aPosition, 1.0);
      vPosition = aPosition;
   }
</script>

<script id='my_fragment_shader' type='x-shader/x-fragment'>
   precision mediump float;
   uniform float uTime;
   uniform vec3  uCursor;
   varying vec3  vPosition;
   vec4 sphere;
   vec4 sphere2;
   vec3 material;
   vec3 Lrgb;
   vec3 Ldir;
    
    float computeZ(vec2 xy, float r) {
      float zz = (r * r - xy.x * xy.x - xy.y * xy.y)/.5;
      if (zz < 0.)
         return -1.;
      else
         return sqrt(zz);
   }
   // Compute intersection of a ray with a sphere, if any.  Return t.
   // If there is no intersection, return 10000.
   float raySphere(vec3 V, vec3 W, vec4 sph) {
        //float r = 1.0;
        //float b = 2.0* dot(V,W);
        //float c = dot(V, V) - (sph.w * sph.w);
        //float h = b*b - 4.0*c;
        //float t = (-b - sqrt(h))/2.0;
        //if(h <0.0  || t < 0.0 ) return 10000.;
        //return t;
        float b = 2.0 * dot(V -= sph.xyz, W);
        float c = dot(V, V) - sph.w * sph.w;
        float d = b * b - 4.0 * c;
        return d < 0.0 ? 10000. : (-b - sqrt(d)) / 2.0;
   }
   // Diffusely shade a sphere.
   //    point is the x,y,z position of the surface point.
   //    sphere is the x,y,z,r definition of the sphere.
   //    material is the r,g,b color of the sphere.
   //vec3 shadeSphere(vec3 point, vec4 sphere, vec3 material, float s) {
   vec3 shadeSphere(vec3 point, vec4 sphere, vec3 material) {
      vec3 color = vec3(1.,2.,4.);
      vec3 N = (point - sphere.xyz) / sphere.w;
      float diffuse = max(dot(Ldir, N), 0.0);
      vec3 ambient = material/5.0;
      //color = ambient + Lrgb *s *diffuse *  max(0.0, dot(N , Ldir));
      color = ambient + Lrgb * diffuse *  max(0.0, dot(N , Ldir));
      return color;
   }

void main(void) {
      vec2 c = uCursor.xy;
      Lrgb = vec3(1.,.5,0.);
      Ldir = normalize(vec3(c.x, c.y, 1. - 2. * dot(c, c)));
      float x = vPosition.x;
      float y = vPosition.y;
      float z = computeZ(vPosition.xy, 1.0);
      // COMPUTE V AND W TO CREATE THE RAY FOR THIS PIXEL,
      // USING vPosition.x AND vPosition.y.
      vec3 V, W;
      W = normalize(vec3( 2.0,0.0,1.0 ));
      vec4 spheres[3];
      if(z > 0.){
      //sphere = vec4(x,y,z,V + dot(W,vec3(1.,1.,1.)));
      //sphere2 = vec4(x+10.,y+10.,z+10.,V + dot(W,vec3(1.,1.,1.)));
      vec2 uv = vPosition.xy/uCursor.xy;
      //generate a ray 
      //V = vec3(0.0, 1.0, 3.0);
      //W = normalize(vec3((-1.0 + 2.0   )*vec2(1.78,1.0), -1.0));
      //SET x,y,z AND r FOR sphere.
      //SET r,g,b FOR material.
      vec3 material = vec3(4., 1., 3.);
      vec3 color = vec3(0., 0., 0.);
      float t = 0.;
      
      for(int i = 0; i < 3; i++){
        
        if(i == 0){
            V  = vec3(2.0,1.0,.0);
            spheres[i] = vec4(x,y,z/2.,V + dot(W,vec3(1.,1.,1.)));
            float t = raySphere(V, W, spheres[i] );
        }
        if(i == 1){
            V  = vec3(100.0,500.0,.0);//attempt to move the vertex of the ray for the second sphere
            spheres[i] = vec4(x,y,z/5.,V + dot(W,vec3(1.,1.,1.)));
            vec3 shift1 = vec3(30.,30.,30.);
            vec3 newPoint = shift1 + V;
            float t = raySphere(newPoint, W, spheres[i] );
        }
        if(i == 2){
            V  = vec3(500.0,1.0,.0); //attempt to move the vertex of the ray for the third sphere
            spheres[i] = vec4(x,y,z/7.,V + dot(W,vec3(1.,1.,1.)));
            vec3 shift2 = vec3(50.,50.,50.);
            vec3 newPoint2 = shift2 + V;
            float t = raySphere(newPoint2, W, spheres[i] );
        }
        //float t2 = raySphere(V, W, sphere2);
        
        //float s = sin((uTime));
        vec3 time = vec3(uTime*2., 1.,1.);
        //float s = tan((tan(sphere.z)/tan((time)*.90+200.0)));
        if (t < 10000.)
            //float s = (sin(sphere.x)/cos(uTime*1.123+200.0));
            //if(i == 0)
                color = shadeSphere(V + t * W, spheres[i], material);
            //if(i == 1)
                //color = shadeSphere(V + t1 * W, sphere[i], material);
            //if(i == 2)
                //color = shadeSphere(V + t1 * W, sphere[i], material);
            //color = shadeSphere(V + t1 * W, sphere, material,s);
        
            //if (t2 < 10000.)
            //color = shadeSphere(V + t2 * W, sphere, material,s);
        
      color.r = 0.5;
      color = pow(color, vec3(.45,.45,.45)); // Do Gamma correction.
      //float d = dot(vec3(x,y,z), vec3(1.,1.,1.));
      //if (d > 0.)
        //    s += 0.6 * d;
      //gl_FragColor = vec4(color, 1.);        // Set opa   city to 1.
      if(i == 0)
        gl_FragColor = vec4((color) * vec3(5.0, 1.0, 4.5), 1.);
      if(i == 1)
        gl_FragColor = vec4((color) * vec3(1.0, 3.0, 7.5), 1.);
      if(i == 2)
        gl_FragColor = vec4((color) * vec3(3.0, 4.0, 8.5), 1.);
      
      }
      } //Close brace for z-check
   }
   
   
</script>

球体 3 的图像似乎渲染在其他两个球体之上

所以如果我做对了:

  1. 你的所有球体都在与投影平面共面的同一平面上
  2. 所有球体都在半径 r=1.0
    • 推导自:float z = computeZ(vPosition.xy, 1.0);
  3. vPosition 是从vertex shader传来的
    • 并包含片段的插值屏幕位置

需要清除的未知事物:

  1. 什么以及如何传递给 GL?

    我得到的印象是你为每个球体传递了一个顶点(作为哪个基元?)这显然是错误的,除非你有几何着色器。但这很难说,因为您的问题没有相关信息。没有制服,没有属性,没有任何插值器,也没有连接到 GLSL

  2. 的代码

怎么做?

我看到 2 个基本选项:

  1. 通过覆盖整个屏幕的单个四边形

    这样片段着色器将循环遍历屏幕的所有像素,因此您需要某种数组,每个球体 x,y,r 每个片段调用都存在。我所知道的唯一可靠的方法是使用纹理

    • 您可以创建 1D RGB 纹理,其中 x=B; y=G; r=R;
    • 或 3x 1D 浮动纹理一个用于 x,y,r
    • 或 1x 1D 浮动纹理,球体被打包成 3 个纹素...

    然后在每个片段调用循环遍历所有球体...

  2. 每个球体传递一个四边形

    因此您可以使用端点 (x0,y0),(x1,y1) 代替每个球体来渲染 2D Quad。

    • 四边形的中间是球心(x,y) = ( 0.5*(x0+x1) , 0.5*(y0+y1) )
    • 球体的半径是r = 0.5*|x1-x0| = 0.5*|y1-y0|

    因此在顶点着色器中计算 x,y,r 并将其传递给片段着色器

[备注]

这两种方式各有千秋pros/cons选择适合自己的

  • 考虑到您的性能需求
  • 以及什么更接近您的做事方式...

看这里Draw Quadratic Curve on GPU这是非常相似的任务