使用多种旋转类型使Skybox旋转而不暴露多维数据集的存在?

时间:2018-03-17 11:29:00

标签: webgl

我开始为一个简单的游戏制作一个Skybox,当我只想使用一个旋转类型转换X,Y或Z时,这一切都很好。

图片:

enter image description here

但是在转向OpenGL之前,有一个问题是我的游戏中我想要一个简单的方块在天空盒内飞行,因此我需要多个旋转类型转换,如X,Y和Z一起旋转相机也围绕相机旋转世界。每当多个旋转变换堆叠在一起时,它们通过暴露其边缘来暴露立方体。

图片:

enter image description here

代码:

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)

1 个答案:

答案 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;
}
`;