永远无法弄清楚如何在 twgl 中做纹理工作

cannot ever figure out how to do texture jobs in twgl

这是我为 vs/fs 准备的代码和一个我想放在 canvas 上的简单立方体。篇幅省略部分内容

<script id="cube-vs" type="notjs">
    precision highhp float;
    attribute vec3 vpos;
    attribute vec3 vnormal;
    attribute vec2 vtex;
    varying vec3 fColor;
    varying vec3 fNormal;
    varying vec2 fTexCoord;

    uniform mat4 view;
    uniform mat4 proj;
    uniform mat4 model;
    uniform vec3 lightdir;
    uniform vec3 cubecolor;

    void main(void) {
        gl_Position = proj * view * model * vec4(vpos, 1.0);
        vec4 normal = normalize(model * vec4(vnormal,0.0));
        float diffuse = .2 + abs(dot(normal, vec4(lightdir,0.0)));
        fColor = (cubecolor * diffuse);
        fTexCoord = vtex;
    }
</script>
<script id="cube-fs" type="notjs">

    precision highhp float;

    varying vec3 fColor;
    varying vec3 fNormal;
    varying vec2 fTexCoord;

    uniform sampler2D texSampler;

    void main(void) {
        vec4 texColor = texture2d(texSampler, fTexCoord);
        gl_FragColor = vec4(fColor*texColor.xyz,1.0);
    }
</script>

立方体

...

    Cube.prototype.init = function(drawingState) {
        var gl=drawingState.gl;
        // create the shaders once - for all cubes
        if (!shaderProgram) {
            shaderProgram = twgl.createProgramInfo(gl, ["tree-vs", "tree-fs"]);
        }
        if (!buffers) {
            var arrays = {
                vpos : { numComponents: 3, data: [...] },
                vnormal : {numComponents:3, data: [...]},
                vtex : {numComponents:2, data: [
                    1,0,0,0,0,1,1,1,
                    1,0,0,0,0,1,1,1,
                    1,0,0,0,0,1,1,1,
                    1,0,0,0,0,1,1,1,
                    1,0,0,0,0,1,1,1,
                    1,0,0,0,0,1,1,1,
                ]},
                indices : {[...]}
                };
            buffers = twgl.createBufferInfoFromArrays(gl,arrays);
        }
        if (!texture) {
            texture = twgl.createTexture(gl, {src:textures/tree.jpg});
        }    
    };
    Cube.prototype.draw = function(drawingState) {
        var modelM = twgl.m4.scaling([this.size*1.4,this.size*1.4,this.size*1.4]);
        twgl.m4.setTranslation(modelM,this.position,modelM);
        var gl = drawingState.gl;
        gl.useProgram(shaderProgram.program);
        twgl.setBuffersAndAttributes(gl,shaderProgram,buffers);
        twgl.setUniforms(shaderProgram,{
            view:drawingState.view, proj:drawingState.proj, lightdir:drawingState.sunDirection,
            cubecolor:this.color, model: modelM, texSampler: texture);
        twgl.drawBufferInfo(gl, gl.TRIANGLES, buffers);
    };

立方体在没有纹理的情况下工作正常 但是当我试着贴上纹理时,它从来没有用过 我几乎尝试了所有方法,但仍然不知道如何添加纹理。 那么,我怎样才能贴上纹理?

如有任何提示,我们将不胜感激。

*********编辑 我已经成功上传了纹理坐标和制服 但是图像没有显示出来,立方体是浅蓝色的。 任何建议将不胜感激。

如果您能提供一个工作示例而不只是部分代码,那就太好了。上面的代码中有几个错别字。例如,在 setUniforms 部分没有结束 }。在 createTexture 部分,url 没有引号。您将 highp 拼写为 highhp,将 texture2D 拼写为 texture2d。我假设如果你说它是 运行 虽然没有纹理那只是转录错误因为如果不是你应该在 JavaScript 控制台中看到非常明显的错误。

不清楚哪里出了问题,但在调试 WebGL 时,我首先要做的是检查 JavaScript 控制台。是否有关于不可渲染纹理的消息?

没有?接下来我要做的就是将片段着色器更改为纯色

<script id="cube-fs" type="notjs">

    precision highhp float;

    varying vec3 fColor;
    varying vec3 fNormal;
    varying vec2 fTexCoord;

    uniform sampler2D texSampler;

    void main(void) {
        vec4 texColor = texture2d(texSampler, fTexCoord);
        gl_FragColor = vec4(fColor*texColor.xyz,1.0);
gl_FragColor = vec4(1,0,0,1);  // -------ADDED-------------------------
    }
</script>

如果您看到立方体,那么是的,问题与纹理有关。如果不是,则问题出在其他地方。

假设您看到一个红色立方体。好的,接下来是检查纹理坐标。我会将片段着色器更改为此

<script id="cube-fs" type="notjs">

    precision highhp float;

    varying vec3 fColor;
    varying vec3 fNormal;
    varying vec2 fTexCoord;

    uniform sampler2D texSampler;

    void main(void) {
        vec4 texColor = texture2d(texSampler, fTexCoord);
        gl_FragColor = vec4(fColor*texColor.xyz,1.0);
gl_FragColor = vec4(fTexCoord,0,1);  // -------CHANGED-------------------------
    }
</script>

您现在应该看到立方体带有红色->绿色阴影。否则你的纹理坐标不好。

如果看起来正确,我可能会尝试的下一件事是将着色器恢复到原来的状态,然后检查变量 texture 是否实际设置。

有很多方法可以检查这个。

  1. 使用WebGL Inspector

  2. 使用标准 JavaScript 调试器。在 setUniforms 部分放置一个断点。检查变量

  3. 做这样的事情

    var uniforms = {
        view:drawingState.view, 
        proj:drawingState.proj, 
        lightdir:drawingState.sunDirection,
        cubecolor:this.color, 
        model: modelM, 
        texSampler: texture,
    };
    window.u = uniforms;
    twgl.setUniforms(shaderProgram, uniforms);
    

    现在打开 JavaScript 控制台并输入 u.texSampler。它应该打印类似

    WebGLTexture {}
    

    如果不是,那么可能 texture 不是您认为的变量

另一个问题是您是否经常使用 requestAnimationFrame 进行渲染,如

function render() {
  // draw stuff like maybe call someCube.draw(drawingState)
  ...
  requestAnimationFrame(render);
}
requestAnimationFrame(render);

我问是因为纹理是异步加载的,所以如果您只渲染一次,那么您将看不到任何纹理,因为它们尚未加载。

你有几个选择。

  1. 不断渲染(如上例)

    twgl 将默认为 1x1 像素纹理,因此渲染应该可以进行。然后图像最终加载它会更新纹理

  2. 等待纹理加载。

    如果您不想在纹理准备好之前渲染,那么您可以向 twgl.createTexture 添加一个回调,它会在纹理准备好时回调您。

另一件事是从工作样本开始

var m4 = twgl.m4;                                              // <!------------ ADDED
var v3 = twgl.v3;                                              // <!------------ ADDED

// not sure why these are globals?!???!
var shaderProgram;                                             // <!------------ ADDED
var texture;                                                   // <!------------ ADDED
var buffers;                                                   // <!------------ ADDED
var canvas = document.querySelector("canvas");                 // <!------------ ADDED
var drawingState = {                                           // <!------------ ADDED
  gl: canvas.getContext("webgl"),                              // <!------------ ADDED
  view: m4.inverse(m4.lookAt([3,3,6], [0, 0, 0], [0, 1, 0])),  // <!------------ ADDED
  proj: m4.perspective(                                        // <!------------ ADDED
    Math.PI * 0.3,                                             // <!------------ ADDED
    canvas.clientWidth / canvas.clientHeight,                  // <!------------ ADDED
    0.1, 10),                                                  // <!------------ ADDED
  sunDirection: v3.normalize([2,3,-2]),                        // <!------------ ADDED
};                                                             // <!------------ ADDED


function Cube() {            // <!-------------------------------------- ADDED
  this.size = 1;             // <!-------------------------------------- ADDED
  this.position = [0, 0, 0]; // <!-------------------------------------- ADDED 
  this.color = [1, 1, 1];    // <!-------------------------------------- ADDED
}                            // <!-------------------------------------- ADDED

Cube.prototype.init = function(drawingState) {
  var gl=drawingState.gl;
  // create the shaders once - for all cubes
  if (!shaderProgram) {
    shaderProgram = twgl.createProgramInfo(gl, ["cube-vs", "cube-fs"]);  // <!---- CHANGED
  }
  if (!buffers) {
    var arrays = {
      vpos: { numComponents: 3, data: [1, 1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1], },
      vnormal:  { numComponents: 3, data:  [1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1], },
      vtex: { numComponents: 2, data: [1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1], },
      indices:  [0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23],
    };
    buffers = twgl.createBufferInfoFromArrays(gl,arrays);
  }
  if (!texture) {
    texture = twgl.createTexture(gl, {
      src: "https://farm6.staticflickr.com/5795/21506301808_efb27ed699_q_d.jpg",
      crossOrigin: "", // <!--------- not needed if on same server which "texture/tree.jpg" is
    });
  }    
};

Cube.prototype.draw = function(drawingState) {
  var modelM = twgl.m4.scaling([this.size*1.4,this.size*1.4,this.size*1.4]);
  twgl.m4.setTranslation(modelM,this.position,modelM);
  var gl = drawingState.gl;
  gl.useProgram(shaderProgram.program);
  twgl.setBuffersAndAttributes(gl,shaderProgram,buffers);
  twgl.setUniforms(shaderProgram,{
    view:drawingState.view, proj:drawingState.proj, lightdir:drawingState.sunDirection,
    cubecolor:this.color, model: modelM, texSampler: texture});
  twgl.drawBufferInfo(gl, gl.TRIANGLES, buffers);
};

var cube = new Cube();               // <!------------ ADDED
cube.init(drawingState);             // <!------------ ADDED

function render() {                  // <!------------ ADDED
  var gl = drawingState.gl           // <!------------ ADDED
  gl.enable(gl.DEPTH_TEST);          // <!------------ ADDED
  cube.draw(drawingState);           // <!------------ ADDED
  requestAnimationFrame(render);     // <!------------ ADDED
}                                    // <!------------ ADDED
requestAnimationFrame(render);       // <!------------ ADDED
canvas { border: 1px solid black; }
<script id="cube-vs" type="notjs">
    //precision highp float;    <!---------------- CHANGED
    attribute vec3 vpos;
    attribute vec3 vnormal;
    attribute vec2 vtex;
    varying vec3 fColor;
    varying vec3 fNormal;
    varying vec2 fTexCoord;

    uniform mat4 view;
    uniform mat4 proj;
    uniform mat4 model;
    uniform vec3 lightdir;
    uniform vec3 cubecolor;

    void main(void) {
        gl_Position = proj * view * model * vec4(vpos, 1.0);
        vec4 normal = normalize(model * vec4(vnormal,0.0));
        float diffuse = .2 + abs(dot(normal, vec4(lightdir,0.0)));
        fColor = (cubecolor * diffuse);
        fTexCoord = vtex;
    }
</script>
<script id="cube-fs" type="notjs">

    precision highp float;  // <!--------- CHANGED (should probably use mediump though)

    varying vec3 fColor;
    varying vec3 fNormal;
    varying vec2 fTexCoord;

    uniform sampler2D texSampler;

    void main(void) {
        vec4 texColor = texture2D(texSampler, fTexCoord);  // < !-------- CHANGED
        gl_FragColor = vec4(fColor*texColor.xyz,1.0);
    }
</script>
<canvas></canvas>
<script src="https://twgljs.org/dist/twgl-full.min.js"></script>

还有一个问题,您 运行 是网络服务器吗? WebGL 需要一个网络服务器来读取纹理。请注意,运行 Web 服务器是微不足道的。 A really good one is here. Just download the version for your OS 然后

path/to/devd-download/devd path/to/project

然后去http://localhost:8000/nameOfYourHtmlFile.html

我假设这不是问题,因为如果是的话,您会在 JavaScript 控制台中看到一个关于无法加载纹理的明显错误,但您没有提到错误JavaScript 控制台