我开始为一个简单的游戏制作一个Skybox,当我只想使用一个旋转类型转换X,Y或Z时,这一切都很好。
图片:
但是在转向OpenGL之前,有一个问题是我的游戏中我想要一个简单的方块在天空盒内飞行,因此我需要多个旋转类型转换,如X,Y和Z一起旋转相机也围绕相机旋转世界。每当多个旋转变换堆叠在一起时,它们通过暴露其边缘来暴露立方体。
图片:
代码:
let canvas = document.querySelector("canvas");
let gl = canvas.getContext("webgl");
gl.canvas.width = canvas.getBoundingClientRect().width;
gl.canvas.height = canvas.getBoundingClientRect().height;
let sliders = [
{
label : "rotationX",
valueStart : 0,
valueEnd : 360,
valueCurrent : 0,
measurement : "°"
}, {
label : "rotationY",
valueStart : 0,
valueEnd : 360,
valueCurrent : 0,
measurement : "°"
}, {
label: "rotationZ",
valueStart : 0,
valueEnd : 360,
valueCurrent : 0,
measurement: "°"
}, {
label: "translationCamera",
valueStart : -2,
valueEnd : 2,
valueCurrent : 0,
measurement: "t"
}
];
setSliders(sliders, drawScene, true, gl);
let vertexShaderSource = `
attribute vec4 a_position;
attribute vec3 a_texture;
varying vec3 v_texture;
uniform mat4 u_matrix;
void main() {
vec4 pos = u_matrix*a_position;
pos.z /= 2.0*sqrt(2.0); //Given the length of cube' side, the maximum value of z is 2.0*sqrt(2.0), bring it back to 1.0 or -1.0 if min value.
gl_Position = vec4(pos.xyz, 1.0);
v_texture = a_position.xyz / sqrt(2.0); //a_position is [-sqrt(2.0), sqrt(2.0)]
}
`;
let fragmentShaderSource = `
precision mediump float;
varying vec3 v_texture;
uniform samplerCube u_skybox;
void main() {
gl_FragColor = textureCube(u_skybox, v_texture);
}
`;
function createCube(x1, y1, length) {
return [
//Blue
x1+length, y1, length/2,
x1+length, y1+length, length/2,
x1+length, y1+length, -length/2,
x1+length, y1+length, -length/2,
x1+length, y1, -length/2,
x1+length, y1, length/2,
//Green
x1, y1, length/2,
x1, y1, -length/2,
x1, y1+length, -length/2,
x1, y1+length, -length/2,
x1, y1+length, length/2,
x1, y1, length/2,
//Yellow
x1, y1+length, length/2,
x1+length, y1+length, -length/2,
x1+length, y1+length, length/2,
x1+length, y1+length, -length/2,
x1, y1+length, length/2,
x1, y1+length, -length/2,
//Light Blue
x1, y1, length/2,
x1+length, y1, length/2,
x1+length, y1, -length/2,
x1+length, y1, -length/2,
x1, y1, -length/2,
x1, y1, length/2,
//Rose
x1, y1, length/2,
x1, y1+length, length/2,
x1+length, y1+length, length/2,
x1+length, y1+length, length/2,
x1+length, y1, length/2,
x1, y1, length/2,
//Red
x1, y1, -length/2,
x1+length, y1+length, -length/2,
x1, y1+length, -length/2,
x1+length, y1, -length/2,
x1+length, y1+length, -length/2,
x1, y1, -length/2
];
}
function createShader(gl, type, source) {
let shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
let success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if(success)
return shader;
console.log(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
}
function createProgram(gl, vertexShader, fragmentShader) {
let program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
let success = gl.getProgramParameter(program, gl.LINK_STATUS);
if(success)
return program;
console.log(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
}
function drawScene(gl) {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
let rotationXCamera = matrices["rotationX"](Math.sin(sliders[0].valueCurrent/360*2*Math.PI), Math.cos(sliders[0].valueCurrent/360*2*Math.PI));
let rotationYCamera = matrices["rotationY"](Math.sin(sliders[1].valueCurrent/360*2*Math.PI), Math.cos(sliders[1].valueCurrent/360*2*Math.PI));
let rotationZCamera = matrices["rotationZ"](Math.sin(sliders[2].valueCurrent/360*2*Math.PI), Math.cos(sliders[2].valueCurrent/360*2*Math.PI));
let perspectiveMatrix = matrices["perspective"](1.0472, gl.canvas.clientWidth / gl.canvas.clientHeight, 1, 2000);
let viewCamera = multiplyMatrices(rotationXCamera, rotationYCamera, rotationZCamera);
let viewMatrix = inverseMatrix(viewCamera);
gl.uniformMatrix4fv(matrixCameraLocation, false, multiplyMatrices(viewMatrix));
gl.drawArrays(gl.TRIANGLES, 0, 36);
}
let vertexPositionLocation, matrixCameraLocation;
let bufferPosition;
function resize(gl) {
let realToCSSPixels = window.devicePixelRatio;
let displayWidth = Math.floor(gl.canvas.clientWidth * realToCSSPixels);
let displayHeight = Math.floor(gl.canvas.clientHeight * realToCSSPixels);
if (gl.canvas.width !== displayWidth ||
gl.canvas.height !== displayHeight) {
gl.canvas.width = displayWidth;
gl.canvas.height = displayHeight;
}
}
let imgs = [];
let promises = [];
[
"Left",
"Right",
"Up",
"Down",
"Front",
"Back"
].forEach((path) => {
let img = document.createElement("img");
img.crossOrigin = "null";
img.src = "http://localhost:8000/texture?filename=FullMoon" + path + "2048.png";
let res;
promises.push(new Promise((resolve, reject) => {
res = resolve;
}));
img.addEventListener("load", res);
imgs.push(img);
});
Promise.all(promises).then(() => {
startWebGL(gl);
});
function isPowerOf2(value) {
return (value & (value - 1)) === 0;
}
function startWebGL(gl) {
resize(gl);
let vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
let fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
let program = createProgram(gl, vertexShader, fragmentShader);
vertexPositionLocation = gl.getAttribLocation(program, "a_position");
matrixCameraLocation = gl.getUniformLocation(program, "u_matrix");
let texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
for(let g = 0; g < 6; g++) {
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X+g, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imgs[g]);
}
if(isPowerOf2(imgs[0].width) && isPowerOf2(imgs[0].height)) {
gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
} else {
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
}
bufferPosition = gl.createBuffer();
gl.useProgram(program);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.enable(gl.CULL_FACE);
gl.disable(gl.DEPTH_TEST);
gl.depthMask(false);
gl.enableVertexAttribArray(vertexPositionLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, bufferPosition);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(createCube(-Math.sqrt(2), -Math.sqrt(2), Math.sqrt(2)*2)), gl.STATIC_DRAW);
gl.vertexAttribPointer(vertexPositionLocation, 3, gl.FLOAT, false, 0, 0);
drawScene(gl);
}
我正在使用长度为sqrt(2.0)的立方体,因为在旋转x时,y在每种情况下都应该至少为1.0 ......
如何软化边缘?任何想法将不胜感激。
修改
遵循gman提议,这里是带有单个四边形的精炼版本,其中立方体贴图映射到它:
let canvas = document.querySelector("canvas");
let gl = canvas.getContext("webgl");
gl.canvas.width = canvas.getBoundingClientRect().width;
gl.canvas.height = canvas.getBoundingClientRect().height;
let sliders = [
{
label : "rotationX",
valueStart : 0,
valueEnd : 2*Math.PI,
value : 0,
measurement : "°"
}, {
label : "rotationY",
valueStart : 0,
valueEnd : 2*Math.PI,
value : 0,
measurement : "°"
}, {
label: "rotationZ",
valueStart : 0,
valueEnd : 2*Math.PI,
value : 0,
measurement: "°"
}
];
setSliders(sliders, drawScene, true, gl);
let vertexShaderSource = `
attribute vec4 a_position;
attribute vec3 a_texture;
uniform mat4 u_matrix;
varying vec3 v_texture;
void main() {
vec4 pos = u_matrix*a_position;
gl_Position = vec4(a_position.xy, 0, 1.0);
v_texture = pos.xyz;
}
`;
let fragmentShaderSource = `
precision mediump float;
uniform samplerCube u_skybox;
varying vec3 v_texture;
void main() {
gl_FragColor = textureCube(u_skybox, v_texture);
}
`;
function createFacade() {
return [
-1, -1, -1,
1, 1, -1,
-1, 1, -1,
1, 1, -1,
-1, -1, -1,
1, -1, -1
];
}
function createShader(gl, type, source) {
let shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
let success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if(success)
return shader;
console.log(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
}
function createProgram(gl, vertexShader, fragmentShader) {
let program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
let success = gl.getProgramParameter(program, gl.LINK_STATUS);
if(success)
return program;
console.log(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
}
function drawScene(gl) {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
let quaternion = matrices["quarternion"]();
let quaternionRot = matrices["fromEuler"](quaternion,
sliders[0].value/Math.PI*360,
sliders[1].value/Math.PI*360,
sliders[2].value/Math.PI*360);
let quaternionMatrix = matrices["fromQuat"](matrices["idMatrix"](), quaternionRot);
let viewMatrix = inverseMatrix(quaternionMatrix);
gl.uniformMatrix4fv(matrixCameraLocation, false, viewMatrix);
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
let vertexPositionLocation, matrixCameraLocation;
let bufferPosition;
function resize(gl) {
let realToCSSPixels = window.devicePixelRatio;
let displayWidth = Math.floor(gl.canvas.clientWidth * realToCSSPixels);
let displayHeight = Math.floor(gl.canvas.clientHeight * realToCSSPixels);
if (gl.canvas.width !== displayWidth ||
gl.canvas.height !== displayHeight) {
gl.canvas.width = displayWidth;
gl.canvas.height = displayHeight;
}
}
let imgs = [];
let promises = [];
[
"Left",
"Right",
"Up",
"Down",
"Front",
"Back"
].forEach((path) => {
let img = document.createElement("img");
img.crossOrigin = "null";
img.src = "http://localhost:8000/texture?filename=./SunSet/SunSet" + path + "2048.png";
let res;
promises.push(new Promise((resolve, reject) => {
res = resolve;
}));
img.addEventListener("load", res);
imgs.push(img);
});
Promise.all(promises).then(() => {
startWebGL(gl);
});
function isPowerOf2(value) {
return (value & (value - 1)) === 0;
}
function startWebGL(gl) {
resize(gl);
let vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
let fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
let program = createProgram(gl, vertexShader, fragmentShader);
vertexPositionLocation = gl.getAttribLocation(program, "a_position");
matrixCameraLocation = gl.getUniformLocation(program, "u_matrix");
let texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture);
for(let g = 0; g < 6; g++) {
gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X+g, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imgs[g]);
}
if(isPowerOf2(imgs[0].width) && isPowerOf2(imgs[0].height)) {
gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
} else {
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
}
bufferPosition = gl.createBuffer();
gl.useProgram(program);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.enable(gl.CULL_FACE);
gl.disable(gl.DEPTH_TEST);
gl.depthMask(false);
gl.enableVertexAttribArray(vertexPositionLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, bufferPosition);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(createFacade()), gl.STATIC_DRAW);
gl.vertexAttribPointer(vertexPositionLocation, 3, gl.FLOAT, false, 0, 0);
drawScene(gl)
答案 0 :(得分:0)
我通过运气和尝试各种可能性找到了:p,而不是围绕相机旋转世界又称立方体,我改变立方体以保持静态,因此我将世界旋转应用于纹理而不是立方体,并且它完美无瑕地工作:
更改的顶点着色器:
let vertexShaderSource = `
attribute vec4 a_position;
attribute vec3 a_texture;
varying vec3 v_texture;
uniform mat4 u_matrix;
void main() {
vec4 pos = u_matrix*a_position;
pos.xyz /= 2.0*sqrt(2.0); //Here I divide by the maximum magnitude so that the vector will become unit vector
gl_Position = vec4(a_position.xy, a_position.z/sqrt(2.0), 1.0); //Doing the same trick as in the question' code
v_texture = pos.xyz;
}
`;