3

Im trying to rotate plane from two triangles in shader. everything goes fine except i cant understand what with the UV coordinates values happens when i applying rotation.

How i can perform this rotation with expected results?

enter image description here

enter image description here

this looks as UV coordinates interpolated independent for each triangle.

let pid, rotateYlocation;
let gl = canvas.getContext('webgl');
let values = document.querySelector('span')
let loader = new Image();
loader.crossOrigin = "anonymous";
loader.src = "https://i.imgur.com/G9H683l.jpg";
loader.onload = function() { 
    canvas.width = loader.width;
    canvas.height = loader.height;
    pid = gl.createProgram();

    shader(`   
        float perspective = 1.0;          
        attribute vec2 coords;
        uniform float rotateY; 
        varying vec2 uv;

        void main(void) {
          mat3 rotY = mat3(vec3( cos(rotateY),  0.0, sin(rotateY)), 
                           vec3( 0.0,           1.0,          0.0),
                           vec3( -sin(rotateY), 0.0, cos(rotateY)));
          vec3 p =  vec3(coords.xy, 0.) * rotY;
          uv = coords.xy.xy*0.5 + 0.5;   
          gl_Position = vec4(p / (1.0 + p.z * perspective), 1.0);
      }
    `, gl.VERTEX_SHADER);

    shader(`
      precision highp float;

      uniform sampler2D texture;
      varying vec2 uv;

      void main(void) {
        gl_FragColor = texture2D(texture, uv);
      }
    `, gl.FRAGMENT_SHADER);

    gl.linkProgram(pid);
    gl.useProgram(pid);

    let array = new Float32Array([-1.0, -1.0,   1.0, -1.0,   -1.0, 1.0, 
                                  -1.0,  1.0,   1.0, -1.0,    1.0, 1.0]);
    gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
    gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW);

    let al = gl.getAttribLocation(pid, "coords");
    gl.vertexAttribPointer(al, 2, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(al);

    let texture = gl.createTexture();
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true)
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, loader);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_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);
    gl.uniform1i(gl.getUniformLocation(pid, "texture"), 0);
    
    rotateYlocation = gl.getUniformLocation(pid, 'rotateY');

    draw();
  }
  
function draw() {
  gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
  gl.clearColor(0, 0, 0, 0);
  gl.uniform1f(rotateYlocation, rotateY.value/1000-0.5)
  gl.drawArrays(gl.TRIANGLES, 0, 6);
  values.textContent = `rotateY: ${(rotateY.value/1000-0.5).toFixed(3)}`;
}

function shader(src, type) {
  let sid = gl.createShader(type);
  gl.shaderSource(sid, src);
  gl.compileShader(sid);
  var message = gl.getShaderInfoLog(sid);
  gl.attachShader(pid, sid);
  if (message.length > 0) {
    console.log(src.split('\n').map(function (str, i) {
      return ("" + (1 + i)).padStart(4, "0") + ": " + str
    }).join('\n'));
    throw message;
  }
}
<input type="range" id="rotateY" min="0" max="1000" value="500" onmousemove="draw()">
<span></span><br>
<canvas id="canvas" style="zoom:0.4"></canvas>
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Stranger in the Q
  • 3,668
  • 2
  • 21
  • 26
  • @Rabbid76 thank you, this is correct, but can you give me some explanation what i'm doing wrong?, code from my quiestion from webglfundamentals lesson https://webglfundamentals.org/webgl/lessons/webgl-3d-perspective.html – Stranger in the Q Jun 10 '19 at 13:07
  • For a better explanation of how perspective interpolation works see https://webglfundamentals.org/webgl/lessons/webgl-3d-perspective-correct-texturemapping.html – gman Jun 10 '19 at 15:33
  • @gman thank you for suddestion, already reading in progress :) – Stranger in the Q Jun 10 '19 at 15:38

1 Answers1

3

varying variables are interpolated in a perspective correct manner. gl_Position is a Homogeneous coordinates (x, y, z, w). For a perspective correct interpolation, the coordinate has to be set correctly.

When you do

gl_Position = vec4(p / (1.0 + p.z * perspective), 1.0) 

then the perspective interpolation can not be performed correctly, because the coordinate is just a Cartesian coordinate (x, y, z, 1) and this results in a linear interpolation, because w=1.
For a detailed information of perspective correct interpolation see How exactly does OpenGL do perspectively correct linear interpolation?.

A homogeneous coordinate is transformed to the cartesian normalized device coordinate by a Perspective divide.
You have to set the clipspace coordinate, that coordinate before the perspective divide:

gl_Position = vec4(p, 1.0 + p.z * perspective);

let pid, rotateYlocation;
let gl = canvas.getContext('webgl');
let values = document.querySelector('span')
let loader = new Image();
loader.crossOrigin = "anonymous";
loader.src = "https://i.imgur.com/G9H683l.jpg";
loader.onload = function() { 
    canvas.width = loader.width;
    canvas.height = loader.height;
    pid = gl.createProgram();

    shader(`   
        float perspective = 1.0;          
        attribute vec2 coords;
        uniform float rotateY; 
        varying vec2 uv;

        void main(void) {
          mat3 rotY = mat3(vec3( cos(rotateY),  0.0, sin(rotateY)), 
                           vec3( 0.0,           1.0,          0.0),
                           vec3( -sin(rotateY), 0.0, cos(rotateY)));
          vec3 p =  vec3(coords.xy, 0.) * rotY;
          uv = coords.xy.xy*0.5 + 0.5;   
          gl_Position = vec4(p, 1.0 + p.z * perspective);
      }
    `, gl.VERTEX_SHADER);

    shader(`
      precision highp float;

      uniform sampler2D texture;
      varying vec2 uv;

      void main(void) {
        gl_FragColor = texture2D(texture, uv);
      }
    `, gl.FRAGMENT_SHADER);

    gl.linkProgram(pid);
    gl.useProgram(pid);

    let array = new Float32Array([-1.0, -1.0,   1.0, -1.0,   -1.0, 1.0, 
                                  -1.0,  1.0,   1.0, -1.0,    1.0, 1.0]);
    gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
    gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW);

    let al = gl.getAttribLocation(pid, "coords");
    gl.vertexAttribPointer(al, 2, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(al);

    let texture = gl.createTexture();
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true)
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, loader);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_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);
    gl.uniform1i(gl.getUniformLocation(pid, "texture"), 0);
    
    rotateYlocation = gl.getUniformLocation(pid, 'rotateY');

    draw();
  }
  
function draw() {
  gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
  gl.clearColor(0, 0, 0, 0);
  gl.uniform1f(rotateYlocation, rotateY.value/1000-0.5)
  gl.drawArrays(gl.TRIANGLES, 0, 6);
  values.textContent = `rotateY: ${(rotateY.value/1000-0.5).toFixed(3)}`;
}

function shader(src, type) {
  let sid = gl.createShader(type);
  gl.shaderSource(sid, src);
  gl.compileShader(sid);
  var message = gl.getShaderInfoLog(sid);
  gl.attachShader(pid, sid);
  if (message.length > 0) {
    console.log(src.split('\n').map(function (str, i) {
      return ("" + (1 + i)).padStart(4, "0") + ": " + str
    }).join('\n'));
    throw message;
  }
}
<input type="range" id="rotateY" min="0" max="1000" value="500" onmousemove="draw()">
<span></span><br>
<canvas id="canvas" style="zoom:0.4"></canvas>
Rabbid76
  • 202,892
  • 27
  • 131
  • 174