Шейдер GLSL для поддержки раскрашивания и текстурирования

#javascript #glsl #webgl

#javascript #glsl #webgl

Вопрос:

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

получить местоположение:

 shaderProgram.useTextureUniform = gl.getUniformLocation(shaderProgram, "uUseTexture");
  

при рисовании я изменяю значение следующим образом:

     var uUseTexture=false;
    gl.uniform1f(shaderProgram.useTextureUniform, uUseTexture);
  

И сам GLSL:

фрагмент:

 precision mediump float;
uniform sampler2D uSampler;
varying vec2 vTextureCoord;
varying vec4 vColor;
uniform bool uUseTexture;
void main(void) {
    vec4 textureColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
    vec4 texColor = vec4(textureColor.rgb, textureColor.a);
    vec4 vertexColor = vColor; 
    if (!uUseTexture){
        gl_FragColor = vertexColor;
    }
    else{
        gl_FragColor = texColor; 
    }
}
  

вершина:

     attribute vec3 aVertexPosition;
    attribute vec3 aVertexNormal;
    attribute vec2 aTextureCoord;
    attribute vec4 aVertexColor;

    uniform mat4 uMVMatrix;
    uniform mat4 uPMatrix;
    uniform mat3 uNMatrix;

    varying vec2 vTextureCoord;
    varying vec4 vColor;


    void main(void){
        vec4 mvPosition = uMVMatrix * vec4(aVertexPosition, 1.0);
        gl_Position = uPMatrix * mvPosition;
        vTextureCoord = aTextureCoord;
        vColor = aVertexColor;}
  

Комментарии:

1. Как это выглядит, когда задуманное идет не так? gl_FragColor = mix(texColor, VertexColor, 0.5); ?

2. @PauliNieminen — текстура не работает, только цвет

3. Правильно. Я не совсем понял ваш первоначальный вопрос. На мой взгляд, шейдеры выглядят нормально. При выборке текстуры не удается прочитать значения, похоже, текстура не привязана к unit или uSampler или vTextureCoord неправильно настроены для распространения из javascript / vbo в fragment shader.

Ответ №1:

Прежде чем я расскажу вам, как заставить ваш шейдер работать, вам, возможно, не следует делать это таким образом. Вы должны либо

  1. Создайте 2 шейдера

    Создайте один шейдер, использующий текстуру, и другой шейдер, использующий цвета вершин. Это то, что сделали бы почти все профессиональные игровые движки.

  2. Создайте шейдер, который умножает оба цвета, и установите один из них на белый

    Если у вас есть

     gl_FragColor = vertexColor * textureColor;
      

    Тогда, если textureColor равно 1,1,1,1 , это означает, что вы умножаете на 1
    и таким образом, результат просто vertexColor . Аналогично, если vertexColor
    1,1,1,1 затем вы умножаете на 1, и поэтому результат просто
    textureColor

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

     var tex = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, tex);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA,
                  gl.UNSIGNED_BYTE, new Uint8Array([255, 255, 255, 255]));
      

    Затем в любое время, когда вы просто хотите, чтобы цвета вершин связывали эту текстуру с текстурой
    разделите и сообщите сэмплеру, в какой блок вы его вставляете

    Возможно, вы также захотите отключить координаты текстуры

     gl.disableVertexAttribArray(texcoordLocation);
      

    Если вам нужен просто цвет текстуры, вы можете сделать это

     // turn off the attribute
    gl.disableVertexAttribArray(aVertexColorLocation);
    
    // set the attribute's constant value
    gl.vertexAttrib4f(aVertexColorLocation, 1, 1, 1, 1);
      

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

  3. Паули упоминает еще один вариант, который заключается в использовании mix

     uniform float u_mixAmount;
    
    gl_FragColor = mix(textureColor, vertexColor, u_mixAmount);
      

    Это также будет работать, поскольку вы можете установить u_mixAmount значение 0.0, когда захотите
    textureColor и до версии 1.0, когда вы хотите vertexColor , но в отличие от вашего
    логический пример вы также можете переходить между двумя цветами со значениями
    между 0.0 и 1.0. Например, 0.3 составляет 30% от vertexColor и 70%
    из textureColor

Несколько других вещей

Эта строка

 vec4 texColor = vec4(textureColor.rgb, textureColor.a);
  

Ничем не отличается от

 vec4 texColor = textureColor;
  

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

 var gl = document.querySelector("canvas").getContext("webgl");
var m4 = twgl.m4;

var arrays = {
  aVertexPosition: [
    -1, -1, 0,
     1, -1, 0,
    -1,  1, 0,
     1,  1, 0,
  ],
  aVertexNormal: [
     0,  0,  1,
     0,  0,  1,
     0,  0,  1,
     0,  0,  1,
  ],
  aTextureCoord: [
     0,  0, 
     1,  0,
     0,  1,
     1,  1,
  ],
  aVertexColor: [
     1, 0, 0, 1,
     0, 1, 0, 1,
     0, 0, 1, 1,
     1, 0, 1, 1,
  ],
  indices: [
    0, 1, 2,
    2, 1, 3,
  ],
};
    
var tex = twgl.createTexture(gl, {
  format: gl.LUMINANCE,
  mag: gl.NEAREST,
  src: [224, 64, 128, 192],
});
var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
var programInfo = twgl.createProgramInfo(gl, ['vs', 'fs']);
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
for (var i = 0; i < 2;   i) {
  twgl.setUniforms(programInfo, {
    uMVMatrix: m4.identity(),
    uPMatrix: m4.scale(m4.translation([i === 0 ? -0.5 : 0.5, 0, 0]), [0.5, 1, 1]),
    uNMatrix: m4.identity(),
    uSampler: tex,
    uUseTexture: i === 1,
  });
  twgl.drawBufferInfo(gl, bufferInfo);
}  
 canvas { border: 1px solid black; }  
 <script src="https://twgljs.org/dist/2.x/twgl-full.min.js"></script>
<script id="fs" type="not-js">
precision mediump float;
uniform sampler2D uSampler;
varying vec2 vTextureCoord;
varying vec4 vColor;
uniform bool uUseTexture;
void main(void) {
    vec4 textureColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
    vec4 texColor = vec4(textureColor.rgb, textureColor.a);
    vec4 vertexColor = vColor; 
    if (!uUseTexture){
        gl_FragColor = vertexColor;
    }
    else{
        gl_FragColor = texColor; 
    }
}
</script>
<script id="vs" type="not-js">
    attribute vec3 aVertexPosition;
    attribute vec3 aVertexNormal;
    attribute vec2 aTextureCoord;
    attribute vec4 aVertexColor;

    uniform mat4 uMVMatrix;
    uniform mat4 uPMatrix;
    uniform mat3 uNMatrix;

    varying vec2 vTextureCoord;
    varying vec4 vColor;


    void main(void){
        vec4 mvPosition = uMVMatrix * vec4(aVertexPosition, 1.0);
        gl_Position = uPMatrix * mvPosition;
        vTextureCoord = aTextureCoord;
        vColor = aVertexColor;}
</script>
<canvas></canvas>  

Комментарии:

1. большое вам спасибо за этот отличный ответ. в любом случае — попробовал то, что сказал Паули, и я получаю только цветную часть, не могу понять почему.

2. как вы сказали, возможно, это ошибка где-то в программе

3. Вы проверяли консоль JavaScript на наличие ошибок? Если ваша текстура настроена неправильно, то она будет просто 0,0,0,0, так что вы не увидите текстуру. Вы хотя бы не видите цвета вершин, когда устанавливаете uUseTexture значение true ?

4. Я добавил gl.disableVertexAttribArray(ShaderProgram.vertexColorAttribute); перед использованием текстуры.