在顶点着色器中应用 Y 旋转时纹理坐标发生偏移
Texture coordinates shift when apply Y rotation in vertex shader
我正在尝试从着色器中的两个三角形旋转平面。
一切顺利,除了我无法理解 UV
坐标值在我应用旋转时会发生什么。
我如何执行此轮换并获得预期结果?
这看起来像是为每个三角形独立插值的 UV 坐标。
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>
varying
变量以正确的透视方式进行插值。 gl_Position
是一个 Homogeneous coordinates (x, y, z, w)。对于透视正确的插值,必须正确设置坐标。
当你这样做时
gl_Position = vec4(p / (1.0 + p.z * perspective), 1.0)
则无法正确进行透视插值,因为坐标只是一个Cartesian coordinate(x, y, z, 1) 而这会导致线性插值,因为w=1.
有关透视正确插值的详细信息,请参阅 How exactly does OpenGL do perspectively correct linear interpolation?。
通过 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>
我正在尝试从着色器中的两个三角形旋转平面。
一切顺利,除了我无法理解 UV
坐标值在我应用旋转时会发生什么。
我如何执行此轮换并获得预期结果?
这看起来像是为每个三角形独立插值的 UV 坐标。
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>
varying
变量以正确的透视方式进行插值。 gl_Position
是一个 Homogeneous coordinates (x, y, z, w)。对于透视正确的插值,必须正确设置坐标。
当你这样做时
gl_Position = vec4(p / (1.0 + p.z * perspective), 1.0)
则无法正确进行透视插值,因为坐标只是一个Cartesian coordinate(x, y, z, 1) 而这会导致线性插值,因为w=1.
有关透视正确插值的详细信息,请参阅 How exactly does OpenGL do perspectively correct linear interpolation?。
通过 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>