【问题标题】:How to render multiple spheres in a glsl fragment shader如何在 glsl 片段着色器中渲染多个球体
【发布时间】:2015-12-26 07:27:50
【问题描述】:

我编写了一个片段着色器,它使用简单的光线追踪在片段着色器内渲染一个球体,对形状进行漫反射。我可以让它适用于一个领域;但是,当我尝试制作多个球体时,只能看到一个。在着色器的 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() 函数对球体进行着色;但是,我目前的逻辑也不适用于多个领域。 turbulence 和 fractal 函数只是添加程序纹理,它们很长,所以我不会添加它们的代码。

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

编辑:

*似乎球体是在彼此之上渲染的,因为我有一个 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 的图像似乎呈现在其他两个球体之上

【问题讨论】:

  • 详细信息在哪里?你如何将球体 x,y,z,r 传递给着色器?你使用什么样的光线追踪(向前,向后,光线的属性是什么......)?我们不知道您的部分代码背后是什么,并且怀疑有人会尝试对其进行解码...通常添加一些描述代码的作用和方式的文本是一个好主意(代码中的 cmets 仅对于那些现在代码应该做什么以及如何做)...
  • @Spektre 是的,很抱歉。我在帖子中添加了一些额外的信息。如果需要任何其他说明,请告诉我。

标签: glsl webgl fragment-shader raytracing


【解决方案1】:

如果我做对了:

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

需要清除未知的东西:

  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。

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

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

[备注]

这两种方式各有优劣,所以选择哪一种更适合你

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

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

【讨论】:

  • 感谢您的回答!现在看来,我至少要使用迭代解决方案(即#1)来计算不同的领域;然而,似乎当它们被渲染时,它们被叠加在另一个之上。你知道如何在不同的位置渲染球体(不修改顶点着色器)吗?我尝试移动光线的顶点原点,但这似乎没有效果。
  • @loremIpsum1771 两种方法都需要对着色器进行轻微更改,因此请显示更改后的代码......并且输出的一些图像也可以。你如何传递球体(纹理、均匀、?)?
  • 我刚刚用顶点和片段着色器的代码更新了帖子(除了噪声函数的定义)。我有一个可变变量的 vec3,这些变量传递给片段着色器,以计算用于着色球体的射线的顶点位置。
  • @loremIpsum1771 您没有任何带有球体x,y,r 的统一数组,也没有任何具有#1 方法所需的此类信息的纹理。我唯一看到的是硬编码的spheres[] 数组,但是如果我做对了,所有球体都使用相同的中心初始化(x,y),所以当你不使用任何矩阵来旋转视图时(轴是与屏幕相同)它们彼此重叠呈现.​​..
  • @loremIpsum1771 如果您阅读我的spheres[] 列表,如果这只是一个终止,我以负半径结束它......这就是 for 停止的地方......但你也可以有一个变量/常数有多少球体,那么它将是i&lt;n
猜你喜欢
  • 1970-01-01
  • 2021-12-02
  • 2017-04-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-26
相关资源
最近更新 更多