WebGL传递数组着色器

4

我是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);

}

结果完全是黑色的,是否有人知道如何正确访问或创建纹理?

1
欢迎来到SO,我希望你阅读FAQ - Christian Rau
4个回答

4

直观上,我们可能会想要实现多个灯光源,像这样:

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);

I hope this can be helpful


1

您正在使用类型为GL_UNSIGNED_BYTEglTexImage2D,但是您却给它一个浮点数数组(Float32Array)。根据规范,这会导致GL_INVALID_OPERATION错误。

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

编辑:由于您评论中的链接,WebGL似乎确实支持浮点纹理。因此,使用GL_FLOAT类型和Float32Array也应该可以工作。但在这种情况下,您必须确保您的硬件也支持浮点纹理(自~GeForce 6以来),并且您的WebGL实现支持OES_texture_float扩展。

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


非常感谢您的回复。但实际上我使用的是gl.FLOAT而不是gl.UNSIGNED_BYTE,但它也没有起作用。我看到了一个使用WebGL的n-body示例,这就是他们处理纹理的方式。所以我不确定唯一的方法是否是使用整数数组。这是示例链接:http://www.ibiblio.org/e-notes/webgl/gpu/n-toy.html - yi chen
@user987058 看起来WebGL支持浮点纹理,并且您可以使用GL_FLOAT类型和Float32Array。但是,在这种情况下,您的图形硬件当然也必须支持浮点纹理。 - Christian Rau
再次感谢。我刚刚发现我的显卡不支持浮点纹理。我认为这就是原因。 - yi chen
如果这个答案解决了你的问题,接受会是一个不错的选择。 - Christian Rau

1
请注意,在WebGL中,与OpenGL相反,您必须明确调用getExtension才能使用扩展(例如OES_texture_float)。然后,您需要将gl.FLOAT作为type参数传递给texImage2D。

-1

规范来看,它似乎也可以使用数组。真正的问题在于类型不匹配。 - Christian Rau
没错。它在数组中的表现非常出色,我经常使用它来创建纯色纹理! - Toji

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接