【问题标题】:WebGL pass array shaderWebGL 传递数组着色器
【发布时间】:2011-10-10 07:27:12
【问题描述】:

我是 WebGL 新手,遇到了一些着色器问题。我想在场景中做多个光源。我在网上搜索,知道在WebGL中,你不能将数组传递给片段着色器,所以唯一的方法是使用纹理。这是我无法弄清楚的问题。

首先,我使用以下代码创建一个 32x32 纹理:

var pix = [];

for(var i=0;i<32;i++)
{

    for(var j=0;j<32;j++)   
        pix.push(0.8,0.8,0.1);
}

gl.activeTexture(gl.TEXTURE0);

gl.bindTexture(gl.TEXTURE_2D, lightMap);
gl.pixelStorei(gl.UNPACK_ALIGNMENT,1);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, 32,32,0, gl.RGB,  gl.UNSIGNED_BYTE,new Float32Array(pix));

gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.uniform1i(g_loader.program.set_uniform["u_texture2"],0);

但是,当我尝试访问着色器中的纹理时:

[片段着色器]

varying vec2 v_texcoord;

uniform sampler2D u_texture2;

void main(void)
{

vec3 lightLoc = texture2D(u_texture, v_texcoord).rgb;

gl_FragData[0] = vec4(lightLoc,1.0);

}

结果是全黑的。有谁知道如何正确访问或创建纹理?

【问题讨论】:

  • 欢迎来到SO,希望您阅读FAQ

标签: arrays textures shader webgl


【解决方案1】:

直观地说,人们会很想实现多个光源,做这样的事情:

uniform int NUM_LIGHTS;
uniform vec3 uLa[NUM_LIGHTS]; 

但是 WebGL 会给你这样的错误:

ERROR: 0:12: ":constant expression required
ERROR: 0:12: ":array size must be a constant integer expression"

尽管如此,您实际上可以将统一数组传递给片段着色器以表示多个光源。唯一需要注意的是,您需要事先知道这些数组的大小。例如:

const int NUM_LIGHTS = 5;
uniform vec3  uLa[NUM_LIGHTS];   //ambient
uniform vec3  uLd[NUM_LIGHTS];   //diffuse
uniform vec3  uLs[NUM_LIGHTS];   //specular

是正确的。您还需要确保在 JavaScript 端映射一个平面数组。所以不要这样做:

var ambientLightArray = [[0.1,0.1,0.1][0.1,0.1,0.1],...]

这样做:

var ambientLightArray = [0.1,0.1,0.1,0.1,0.1,0.1,..]

然后你做:

var location = gl.getUniformLocation(prg,"uLa");
gl.uniform3fv(location, ambientLightArray);

使用预定义大小设置数组后,您可以执行以下操作:

//Example: Calculate diffuse contribution from all lights to the current fragment
//vLightRay[] and vNormal are varyings calculated in the Vertex Shader
//uKa and uKd are material properties (ambient and diffuse)

vec3 COLOR = vec3(0.0,0.0,0.0);
vec3 N = normalize(vNormal);
for(int i = 0; i < NUM_LIGHTS; i++){    
   L = normalize(vLightRay[i]);     
   COLOR += (uLa[i] * uKa) + (uLd[i] * uKd * clamp(dot(N, -L),0.0,1.0));
}   
gl_FragColor =  vec4(COLOR,1.0);

希望对你有帮助

【讨论】:

    【解决方案2】:

    您正在调用glTexImage2D,类型为GL_UNSIGNED_BYTE,但随后您给它一个浮点数组(Float32Array)。根据specification 这会导致GL_INVALID_OPERATION 错误。

    您应该将位置从 [0,1] 浮点数转换为 [0,255] 范围内的整数,并使用 Uint8Array。不幸的是,这会使您失去一些精度,并且您的所有位置都需要在 [0,1] 范围内(或者至少是某个固定范围,稍后您将从纹理中获得的 [0,1] 值转换为)。但我一定要记住,WebGL 目前不支持浮点纹理。

    编辑:由于您评论中的链接,WebGL 似乎确实支持浮点纹理。因此,使用 GL_FLOATFloat32Array 类型也应该有效。但在这种情况下,您必须确保您的硬件也支持浮点纹理(从 ~GeForce 6 开始)并且您的 WebGL 实现支持 OES_texture_float 扩展。

    您也可以尝试将过滤模式设置为GL_NEAREST,因为旧硬件可能不支持线性过滤浮点纹理。而且由于您无论如何都想将纹理用作简单数组,因此不需要任何插值。

    【讨论】:

    • 非常感谢您的回复。
    • 非常感谢您的回复。但实际上我使用的是 gl.FLOAT 而不是 gl.UNSIGNED_BYTE 并且它也不起作用。我看到了一个带有 webGL 的 n-body 样本,这是他们制作纹理的方式。所以我不确定是否唯一的方法是使用整数数组。这是示例的链接:ibiblio.org/e-notes/webgl/gpu/n-toy.html
    • @user987058 似乎 WebGL 支持浮点纹理,您可以使用 GL_FLOAT 类型和 Float32Array。但在这种情况下,您的图形硬件当然也必须支持浮点纹理。
    • @user987058 更新了我的答案。
    • 再次感谢。我刚刚发现我的显卡不支持浮点纹理。我想这就是原因。
    【解决方案3】:

    请注意,在 WebGL 中,与 OpenGL 不同,您必须先显式调用 getExtension,然后才能使用扩展,例如 OES_texture_float。然后你想将 gl.FLOAT 作为类型参数传递给 texImage2D。

    【讨论】:

      【解决方案4】:

      texImage2D 函数需要和 image 作为参数,而不是数组。您应该将纹理写入 Canvas,然后将画布图像用作 texImage2D 参数。

      查看此链接:

      http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#pixel-manipulation

      【讨论】:

      • specification 看来,它似乎也适用于数组。这里真正的问题是类型不匹配。
      • 正确。它适用于数组,我一直使用它来创建纯色纹理!
      猜你喜欢
      • 2013-07-26
      • 1970-01-01
      • 2018-10-27
      • 1970-01-01
      • 1970-01-01
      • 2012-07-15
      • 2014-12-06
      • 1970-01-01
      • 2021-01-07
      相关资源
      最近更新 更多