WebGL pass array shader
I'm new to WebGL and I'm facing some problems of the shaders. I wanna do multiple light sources in the scene. I searched online and knew that in WebGL, you can't pass an array into the fragment shader, so the only way is use the texture. Here is the problem I can't figure out.
First, I create a 32x32 texture using the following code:
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);
But however, when I tried to access the texture in the shader:
[Fragment Shader]
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);
}
The result is totally black. Is there anyone knows how to acces or create the texture correctly?
Intuitively, one would be tempted to implement multiple light sources doing something like this:
uniform int NUM_LIGHTS;
uniform vec3 uLa[NUM_LIGHTS];
But WebGL gives you an error like this:
ERROR: 0:12: ":constant expression required
ERROR: 0:12: ":array size must be a constant integer expression"
Nonetheless, you actually can pass uniform arrays to the Fragment Shader to represent multiple light sources. The only caveat is that you need to know beforehand the size that these arrays will have. For example:
const int NUM_LIGHTS = 5;
uniform vec3 uLa[NUM_LIGHTS]; //ambient
uniform vec3 uLd[NUM_LIGHTS]; //diffuse
uniform vec3 uLs[NUM_LIGHTS]; //specular
Is correct. Also you need to make sure that you map a flat array on the JavaScript side. So instead of doing this:
var ambientLightArray = [[0.1,0.1,0.1][0.1,0.1,0.1],...]
do this:
var ambientLightArray = [0.1,0.1,0.1,0.1,0.1,0.1,..]
Then you do:
var location = gl.getUniformLocation(prg,"uLa");
gl.uniform3fv(location, ambientLightArray);
Once you set up your arrays with a predefined size you can do things like this:
//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
You are calling glTexImage2D
with a type of GL_UNSIGNED_BYTE
, but then you give it an array of floats (Float32Array
). According to the specification This causes a GL_INVALID_OPERATION
error.
You should rather transform your positions from [0,1] floats to integers in the [0,255] range and use a Uint8Array
. Unfortunately this looses you some precision and all your positions need to be in the [0,1] range (or at least some fixed range, which you later transform the [0,1] values you get from the texture into). But I'm sure to remember that WebGL doesn't support floating point textures at the moment.
EDIT: Due to the link in your comment WebGL seems indeed to support floating point textures. So using a type of GL_FLOAT
and a Float32Array
should work, too. But in this case you have to make sure your hardware also supports floating point textures (since ~GeForce 6) and your WebGL implementation supports the OES_texture_float
extension.
You may also try to set the filter modes to GL_NEAREST
, as older hardware may not support linearly filtered floating point textures. And as you want to use the texture as a simple array anyway, you shouldn't need any interpolation.
Note that in WebGL, contrary to OpenGL, you have to explicitly call getExtension before you can use an extension, like OES_texture_float. And then you want to pass gl.FLOAT as the type parameter to texImage2D.
The texImage2D function expects and image as parameter, not an array. You should write your texture to a Canvas and then use the canvas image as the texImage2D parameter.
Check out this link:
http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#pixel-manipulation
精彩评论