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

时间:2015-09-28 23:37:43

标签: glsl webgl fragment-shader raytracing

我编写了一个片段着色器,它使用简单的光线跟踪在片段着色器内部渲染一个漫反射阴影形状的球体。我可以让这个为一个球体工作;然而,当我尝试制作多个球体时,只能看到一个球体。在着色器的main()函数中,我基本上正在做同样的过程,为我想要制作的每个额外的球体渲染一个球体,但这似乎目前没有效果。

其他信息:

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

我想我只需要在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的图像似乎呈现在另外两个球体的顶部

enter image description here

1 个答案:

答案 0 :(得分:2)

所以,如果我做对了:

  1. 你的所有球体都在与投影平面共面的同一平面上
  2. 所有球体都在半径r=1.0
    • 摘自:float z = computeZ(vPosition.xy, 1.0);
  3. vPosition从顶点着色器传递
    • 并包含片段
    • 的插值屏幕位置
  4. 需要清除未知事项:

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

      除非你有几何着色器,否则我得到的印象是每个球体传递一个顶点(就像哪个原始?)显然是错误的。但很难说,因为你的问题没有相关信息。没有制服,没有属性,没有内插器,也没有连接到GLSL的代码

    2. 怎么做?

      我看到了两个基本选项:

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

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

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

        然后在每个片段中调用循环遍历所有领域..​​.

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

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

        • Quad的中间是球体中心(x,y) = ( 0.5*(x0+x1) , 0.5*(y0+y1) )
        • 并且球体的半径为r = 0.5*|x1-x0| = 0.5*|y1-y0|

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

      3. <强> [注释]

        这两种方式各有利弊,所以选择哪种方式更适合你

        • 考虑到您的表现需求
        • 更接近你做事的方式......

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