Никогда не могу понять, как сделать текстурные работы в TWGL

Это код, который у меня есть для vs / fs и простой куб, который я хочу поместить на свой холст. некоторая часть опущена из-за продолжительности.

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

куб работал без текстуры, но когда я пытался нанести текстуру, он никогда не работал, я попробовал почти все и до сих пор не могу понять, как нанести текстуру. Итак, как я могу наложить текстуру?

любые советы будут очень цениться.

********* edit Мне удалось успешно загрузить координаты текстуры и форму, но изображение не отображается, а куб окрашен в светло-синий цвет. любые предложения будут очень признательны.

1 ответ

Было бы неплохо, если бы вы могли предоставить рабочий пример, а не только части кода. В приведенном выше коде есть несколько опечаток. Например, в 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

  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 требует веб-сервер для чтения текстур. Обратите внимание, что запуск веб-сервера является тривиальным. Действительно хороший здесь. Просто скачайте версию для вашей ОС

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

Затем перейдите к http://localhost:8000/nameOfYourHtmlFile.html

Я предполагаю, что это не было проблемой, потому что если бы это было, вы бы увидели явную ошибку в консоли JavaScript о невозможности загрузить текстуру, но вы не упомянули ошибки в консоли JavaScript

Другие вопросы по тегам