Почему мои шейдеры не работают для трапециевидных полигонов?

Мне нужно нарисовать части текстуры на многоугольниках трапеций. (олдскульная фальшивая 3D-гонка, дорога сделана из этих трапеций, и я хочу применить к ним текстуру.)

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

В поисках решения я вижу, что эта проблема распространена и причина в том, что два треугольника не равны, а шейдер 2D. Из того, что я нашел, и особенно из этого ответа: https://stackoverflow.com/a/25239021/3666866, я попытался исправить мой шейдер. Но это ничего не изменило...

My shaders :  (copied from webglfundamentals.com, edited according to https://stackoverflow.com/a/25239021/3666866)

<script id="3d-vertex-shader" type="x-shader/x-vertex">
    attribute vec4 a_position ;
    //attribute vec2 a_texcoord ;
    attribute vec4 a_texcoord ;
    uniform mat4 u_matrix ;
    //varying vec2 v_texcoord ;
    varying vec4 v_texcoord ;
    void main() {
        gl_Position = u_matrix * a_position ;
        v_texcoord = a_texcoord ;
    }
</script>

<script id="3d-fragment-shader" type="x-shader/x-fragment">
    precision mediump float ;
    //varying vec2 v_texcoord ;
    varying vec4 v_texcoord ;
    uniform sampler2D u_texture ;
    void main() {
         //gl_FragColor = texture2D(u_texture, v_texcoord) ;
         gl_FragColor = texture2DProj( u_texture , v_texcoord ) ;
    }
</script>

code :

gl_.positionLocation = gl.getAttribLocation( gl_.program, "a_position" );
gl_.texcoordLocation = gl.getAttribLocation( gl_.program, "a_texcoord" );
gl.matrixLocation = gl.getUniformLocation( gl_.program, "u_matrix" );
gl.textureLocation = gl.getUniformLocation( gl_.program, "u_texture" );
gl_.positionBuffer = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, gl_.positionBuffer );
var positions = new Float32Array(
    [
    -1.5, -0.5,   0.5,
     1.5, -0.5,   0.5,
    -0.5,  0.5,   0.5,
    -0.5,  0.5,   0.5,
     1.5, -0.5,   0.5,
     0.5,  0.5,   0.5,
    ]);
    gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);

gl_.texcoordBuffer = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, gl_.texcoordBuffer );
gl.bufferData(
        gl.ARRAY_BUFFER,
        new Float32Array(
            [
            0.25, 0  ,
            0.5 , 0  ,
            0.25, 0.5,
            0.25, 0.5,
            0.5 , 0  ,
            0.5 , 0.5,
        ]),
        gl.STATIC_DRAW);



The render code :
gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.bindBuffer( gl.ARRAY_BUFFER, positionBuffer );
gl.vertexAttribPointer(positionLocation,3,gl.FLOAT,false,0,0);
gl.enableVertexAttribArray( texcoordLocation );
gl.bindBuffer( gl.ARRAY_BUFFER, texcoordBuffer );
gl.vertexAttribPointer(texcoordLocation,3,gl.FLOAT,false,0,0);
let projectionMatrix = m4.perspective( fieldOfViewRadians, aspect, 1, 2000);
let viewProjectionMatrix = m4.multiply( projectionMatrix, viewMatrix );
let matrix = m4.xRotate( viewProjectionMatrix, modelXRotationRadians );
gl.uniformMatrix4fv( matrixLocation , false , viewProjectionMatrix );
gl.uniform1i( textureLocation , 0 );
gl.drawArrays(gl.TRIANGLES, 0, 6 * 1 );

Где ошибка?


person Diego    schedule 06.07.2019    source источник
comment
Просто любопытно. Каков пример старой школьной фальшивой трехмерной гоночной игры? Я спрашиваю, потому что представляю масштабированные четырехугольники, такие как Outrun или Power Drift, но я не помню, в каких играх использовались трапеции.   -  person gman    schedule 06.07.2019
comment
Что касается вашего кода, вы не показали свои координаты текстуры, но ответ, на который вы ссылаетесь, использует координаты 3D-текстуры, но ваш код выше передает 2 в gl.vertexAttribPointer для координат текстуры, поэтому вы не передаете координаты 3D-текстуры шейдеру.   -  person gman    schedule 06.07.2019
comment
Я плохо объяснил. Я не могу использовать тот же трюк со строкой сканирования, что и в старых играх, поэтому вместо этого я хочу использовать трапеции, результат должен выглядеть очень похоже (надеюсь). Я изменил его на 3, но id не повлиял.   -  person Diego    schedule 06.07.2019
comment
Примеры, на которые вы ссылаетесь, используют координаты 3D-текстуры, но вы используете только координаты 2D-текстуры. Вам нужно добавить правильную третью координату для каждой координаты текстуры, как и ответы, на которые вы ссылались.   -  person gman    schedule 07.07.2019


Ответы (1)


Глядя на примеры, на которые вы ссылаетесь, вам необходимо указать координаты 3D-текстуры. Итак, вместо UV, например

 0, 0,
 0, 1,
 1, 0,
 1, 1,

Каждое из них должно быть умножено на абсолютное значение координаты X точек трапеции, а затем это абсолютное значение X должно быть 3-й координатой.

 0 * X0, 0 * X0, X0,
 0 * X1, 1 * X1, X1,
 1 * X2, 0 * X2, X2,
 1 * X3, 1 * X3, X3,

Это заканчивается теми же координатами текстуры, потому что texture2DProj делит xy каждой координаты на z, но это меняет способ интерполяции координаты текстуры.

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

attribute vec4 position;
attribute vec2 texcoord;

uniform mat4 u_matrix;

varying vec3 v_texcoord;

void main() {
  gl_Position = u_matrix * position;
  v_texcoord = vec3(texcoord.xy, 1) * abs(position.x);
}

пример

'use strict';

/* global twgl, m4, requestAnimationFrame, document */

const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl');

const vs = `
attribute vec4 position;
attribute vec2 texcoord;

uniform mat4 u_matrix;

varying vec3 v_texcoord;

void main() {
  gl_Position = u_matrix * position;
  v_texcoord = vec3(texcoord.xy, 1) * abs(position.x);
}
`;

const fs = `
precision highp float;

varying vec3 v_texcoord;

uniform sampler2D tex;

void main() {
  gl_FragColor = texture2DProj(tex, v_texcoord);
}
`;

// compile shader, link, look up locations
const programInfo = twgl.createProgramInfo(gl, [vs, fs]);

// make some vertex data
const W0 = 1;
const W1 = 0.5;
const bufferInfo = twgl.createBufferInfoFromArrays(gl, {
  position: {
    numComponents: 2,
    data: [
      -1, -1,
       1, -1,
     -.5,  1,
       
       1, -1,
      .5,  1,
     -.5,  1,
    ],
  },
  texcoord: [
     0,  0,  
     1,  0,
     0,  1,

     1,  0,
     1,  1,
     0,  1,
  ],
});

const tex = twgl.createTexture(gl, {
  src: [
    0xC0, 0x80, 0xC0, 0x80,
    0x80, 0xC0, 0x80, 0xC0,
    0xC0, 0x80, 0xC0, 0x80,
    0x80, 0xC0, 0x80, 0xC0,
  ],
  format: gl.LUMINANCE,
  minMag: gl.NEAREST,
});

function render(time) {
  time *= 0.001;
  
  gl.useProgram(programInfo.program);

  // calls gl.bindBuffer, gl.enableVertexAttribArray, gl.vertexAttribPointer
  twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);

  // calls gl.activeTexture, gl.bindTexture, gl.uniformXXX
  twgl.setUniforms(programInfo, {
    u_matrix: m4.rotationZ(time),
  });  

  // calls gl.drawArrays or gl.drawElements
  twgl.drawBufferInfo(gl, bufferInfo);
  
  requestAnimationFrame(render);
}
requestAnimationFrame(render);
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>

person gman    schedule 07.07.2019