WebGL纹理创建问题

3

我正在编写光线追踪程序,主要思路是将.obj模型的三角形和索引保存到2个纹理中:

a) 顶点纹理 b) 索引纹理

我这样做是为了在我的GLSL片段着色器中进行光线-三角形相交,但浏览器告诉我有关纹理的问题:

a) Mozilla Firefox

错误#1(顶点纹理):错误:WebGL:texImage2D:无效类型0x1406。

错误#2(索引纹理):错误:WebGL:texImage2D:无效类型0x1403。

b) Google Chrome

两种纹理都出现错误:webgl invalid_enum teximage2d invalid texture type

以下是代码:

Obj.prototype.initTextures = function(){

this.vertexTexture = gl.createTexture();
gl.activeTexture(gl.TEXTURE0)
gl.bindTexture(gl.TEXTURE_2D, this.vertexTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, this.vertexSize, 1, 0, gl.RGB, gl.FLOAT, new Float32Array(this.vertexArray));
gl.bindTexture(gl.TEXTURE_2D, null);

this.trianglesTexture = gl.createTexture();
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, this.trianglesTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, this.faceSize, 1, 0, gl.RGB,  gl.UNSIGNED_SHORT, new Uint16Array(this.faceArray));
gl.bindTexture(gl.TEXTURE_2D, null);

由于明显的原因,this.vertexArray和this.faceArray包含了相关的数据。

1个回答

12

只有在检查并启用浮点纹理时,才允许使用 gl.RGB / gl.FLOAT 纹理。

var floatTextures = gl.getExtension('OES_texture_float');
if (!floatTextures) {
    alert('no floating point texture support');
    return;
}

gl.RGB / gl.UNSIGNED_SHORT 纹理在WebGL上根本不存在。您可以尝试将无符号短整型的值编码为R * 256 + G * 65536之类的形式。或者也可以在这里使用浮点数。

注意:过滤浮点数,例如gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR),是一个单独的扩展OES_texture_float_linear,但在您的情况下,由于您只使用gl.NEAREST,因此无需检查该扩展。

至于将顶点数据放入纹理中,您可能需要做一些工作来提取正确的值。

要索引纹理中的值,您需要计算一个纹理坐标,以访问正确的纹素(texel)。为此,您需要从第一个纹素的中心到最后一个纹素的中心进行访问。

换句话说,如果我们有3个值(因此有3个纹素),我们会得到类似于这样的结果:

     ------3x1 ----- texels ----------
     [         ][         ][         ]
 0.0 |<----------------------------->| 1.0

如果我们只是用index / numValues来计算纹理坐标,那么结果会是:

     [         ][         ][         ]
     |          |          |
     0.0       0.333       0.666

在Texel之间取值时,需要加上半个Texel才能得到正确结果。

     [         ][         ][         ]
          |          |          |
        0.167       0.5       0.833

希望这有意义。这是一小段代码。

var gl = document.querySelector("canvas").getContext("webgl");
var ext = gl.getExtension("OES_texture_float");
if (!ext) {
  alert("need OES_texture_float extension cause I'm lazy");
  //return;
}
if (gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) < 2) {
  alert("need to be able to access textures from vertex shaders");
  //return;
}

var m4 = twgl.m4;
var v3 = twgl.v3;
var program = twgl.createProgramFromScripts(gl, ["vshader", "fshader"]);

var positionIndexLoc = gl.getAttribLocation(program, "a_positionIndex");
var normalIndexLoc = gl.getAttribLocation(program, "a_normalIndex");

var colorLoc = gl.getUniformLocation(program, "u_color");
var mvpMatrixLoc = gl.getUniformLocation(program, "u_mvpMatrix");
var mvMatrixLoc = gl.getUniformLocation(program, "u_mvMatrix");
var lightDirLoc = gl.getUniformLocation(
  program, "u_lightDirection");
var u_positionsLoc = gl.getUniformLocation(
  program, "u_positions");
var u_normalsLoc = gl.getUniformLocation(
  program, "u_normals");
gl.uniform1i(u_positionsLoc, 0); // use texture unit 0 for positions
gl.uniform1i(u_normalsLoc, 1);   // use texture unit 1 for normals

// Cube data
var positions = [
  -1, -1, -1, // 0 lbb
  +1, -1, -1, // 1 rbb      2---3
  -1, +1, -1, // 2 ltb     /|  /|
  +1, +1, -1, // 3 rtb    6---7 |
  -1, -1, +1, // 4 lbf    | | | |
  +1, -1, +1, // 5 rbf    | 0-|-1
  -1, +1, +1, // 6 ltf    |/  |/
  +1, +1, +1, // 7 rtf    4---5
];
var positionIndices = [
  3, 7, 5, 3, 5, 1, // right
  6, 2, 0, 6, 0, 4, // left
  6, 7, 3, 6, 3, 2, // top
  0, 1, 5, 0, 5, 4, // bottom
  7, 6, 4, 7, 4, 5, // front
  2, 3, 1, 2, 1, 0, // back
];
var normals = [
  +1,  0,  0,
  -1,  0,  0,
  0, +1,  0,
  0, -1,  0,
  0,  0, +1,
  0,  0, -1,
]
  var normalIndices = [
  0, 0, 0, 0, 0, 0,  // right
  1, 1, 1, 1, 1, 1,  // left
  2, 2, 2, 2, 2, 2,  // top
  3, 3, 3, 3, 3, 3,  // bottom
  4, 4, 4, 4, 4, 4,  // front
  5, 5, 5, 5, 5, 5,  // back
];

function degToRad(deg) {
  return deg * Math.PI / 180;
}

function uploadIndices(loc, data, indices) {
  // scale indices into texture coordinates
  var scaledIndices = new Float32Array(indices.length);

  // to index the value in the texture we need to
  // compute a texture coordinate that will access
  // the correct texel. To do that we need access from
  // the middle of the first texel to the middle of the
  // last texel.
  //
  // In other words if we had 3 values (and therefore
  // 3 texels) we'd have something like this
  //
  //     ------3x1 ----- texels ----------
  //     [         ][         ][         ]
  // 0.0 |<----------------------------->| 1.0
  //
  // If we just did index / numValues we'd get
  //
  //     [         ][         ][         ]
  //     |          |          |
  //     0.0       0.333       0.666
  //
  // Which is right between texels so we add a
  // a halfTexel to get this
  //
  //     [         ][         ][         ]
  //          |          |          |
  //        0.167       0.5       0.833


  var size = data.length / 3;
  var texel = 1 / size;
  var halfTexel = texel / 2;
  for (var ii = 0; ii < indices.length; ++ii) {
    scaledIndices[ii] = indices[ii] / size + halfTexel;
  }
  var buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER,
                scaledIndices,
                gl.STATIC_DRAW);
  gl.enableVertexAttribArray(loc);
  gl.vertexAttribPointer(loc, 1, gl.FLOAT, false, 0, 0);
}

function uploadTexture(unit, data) {
  gl.activeTexture(gl.TEXTURE0 + unit);
  var tex = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, tex);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB,
                data.length / 3, 1, 0,
                gl.RGB, gl.FLOAT, new Float32Array(data));
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER,
                   gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER,
                   gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S,
                   gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T,
                   gl.CLAMP_TO_EDGE);
}

uploadIndices(positionIndexLoc, positions, positionIndices);
uploadIndices(normalIndexLoc, normals, normalIndices);
uploadTexture(0, positions);
uploadTexture(1, normals);

var xRot = 30;
var yRot = 20;
var zRot = 0;

var lightDir = v3.normalize([-0.2, -0.1, 0.5]);

function draw() {
  xRot += 0;
  yRot += 1;
  
  twgl.resizeCanvasToDisplaySize(gl.canvas);
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  
  gl.enable(gl.DEPTH_TEST);
  gl.enable(gl.CULL_FACE);
  gl.clearColor(0, 0, 0, 1);
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

  gl.useProgram(program);  

  var projection = m4.perspective(
    degToRad(45),
    gl.canvas.clientWidth / gl.canvas.clientHeight,
    0.1, 100.0);

  var world = m4.identity();
  world = m4.translate(world, [0.0, 0.0, -5.0]);

  world = m4.rotateX(world, degToRad(xRot));
  world = m4.rotateY(world, degToRad(yRot));
  world = m4.rotateZ(world, degToRad(zRot));

  var mvp = m4.multiply(projection, world);

  gl.uniformMatrix4fv(mvMatrixLoc, false, world);
  gl.uniformMatrix4fv(mvpMatrixLoc, false, mvp);

  gl.uniform4f(colorLoc, 0.5, 0.8, 1, 1);
  gl.uniform3fv(lightDirLoc, lightDir);

  gl.drawArrays(gl.TRIANGLES, 0, 36);
  requestAnimationFrame(draw);
}
draw();
body {
  margin: 0;
}
canvas {
  width: 100vw;
  height: 100vh;
  display: block;
}
<script src="//twgljs.org/dist/2.x/twgl-full.min.js"></script>
<script id="vshader" type="whatever">
attribute float a_positionIndex;
attribute float a_normalIndex;
attribute vec4 a_pos;

uniform sampler2D u_positions;
uniform sampler2D u_normals;
uniform mat4 u_mvpMatrix;
uniform mat4 u_mvMatrix;

varying vec3 v_normal;

void main() {
  vec3 position = texture2D(
      u_positions, vec2(a_positionIndex, 0.5)).rgb;
  vec3 normal = texture2D(
      u_normals, vec2(a_normalIndex, 0.5)).rgb;
  gl_Position = u_mvpMatrix * vec4(position, 1);
  v_normal = (u_mvMatrix * vec4(normal, 0)).xyz;
}
</script>
<script id="fshader" type="whatever">
precision mediump float;

uniform vec4 u_color;
uniform vec3 u_lightDirection;

varying vec3 v_normal;

void main() {
  float light = dot(
      normalize(v_normal), u_lightDirection) * 0.5 + 0.5;
  gl_FragColor = vec4(u_color.rgb * light, u_color.a);
}
</script>
<canvas></canvas>


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