计算WebGL的斜投影

问题描述

我正在尝试在WebGL 2.0中创建门户网站呈现引擎(到目前为止,我正在尝试移植此C ++项目:https://github.com/HackerPoet/NonEuclidean),并且正在尝试实现倾斜投影,以对齐对象的剪切平面。门户摄像机到各自的门户;尽管在各种OpenGL平台上找到了多种实现,但是,我无法在我的WebGL项目中使用它。以下是与此相关的代码

const Mat4 = (() =>
{
    const { mat3,mat4,vec2,vec3,vec4 } = glMatrix;
    return {
        ...mat4,zAxis(out,mat)
        {
            return vec3.set(out,mat[2],mat[6],mat[10]);
        },mulVec4(out,mat,vec)
        {
            const [x,y,z,w] = vec;
            return vec3.set(out,mat[ 0] * x + mat[ 1] * y + mat[ 2] * z + mat[ 3] * w,mat[ 4] * x + mat[ 5] * y + mat[ 6] * z + mat[ 7] * w,mat[ 8] * x + mat[ 9] * y + mat[10] * z + mat[11] * w,mat[12] * x + mat[13] * y + mat[14] * z + mat[15] * w);
        },mulPoint(out,z] = vec;
            const w = mat[12] * x + mat[13] * y + mat[14] * z + mat[15];
            return vec3.set(out,(mat[0] * x + mat[1] * y + mat[ 2] * z + mat[ 3]) / w,(mat[4] * x + mat[5] * y + mat[ 6] * z + mat[ 7]) / w,(mat[8] * x + mat[9] * y + mat[10] * z + mat[11]) / w);
        },};
})();

const Camera = () =>
{
    const { vec3,vec4 } = glMatrix;
    const projection = Mat4.create();
    const view = Mat4.create();
    return {
        width:  0,height: 0,aspect: 1,near:   0.1,far:    100.0,fov:    Math.PI / 3,pos:    [0,0],euler:  [0,get view()
        {
            return view;
        },set view(matrix)
        {
            return Mat4.copy(view,matrix);
        },get matrix()
        {
            return Mat4.multiply(Mat4.create(),projection,view)
        },get projection()
        {
            return projection;
        },set transform([x = 0,y = 0,z = 0,rx = 0,ry = 0])
        {
            const matrix = Mat4.fromXRotation(Mat4.create(),rx);
            Mat4.multiply(matrix,matrix,Mat4.fromYRotation(Mat4.create(),ry));
            Mat4.multiply(matrix,Mat4.fromTranslation(Mat4.create(),[-x,-y,-z]));
            Mat4.copy(view,matrix);
            return view;
        },inverse()
        {
            const inv = Mat4.create();
            const a = projection[0];
            const b = projection[5];
            const c = projection[10];
            const d = projection[11];
            const e = projection[14];
            inv[0]  = 1 / a;
            inv[5]  = 1 / b;
            inv[11] = 1 / e;
            inv[14] = 1 / d;
            inv[15] = -c / (d * e)
            return inv;
        },clipOblique(pos,norm)
        {
            const cpos = Mat4.mulPoint(vec3.create(),view,pos);
            const cnorm = vec3.normalize(vec3.create(),Mat4.mulPoint(vec3.create(),norm));
            const point = Mat4.mulPoint(vec3.create(),Mat4.invert(Mat4.create(),view),[0,0]);
            cpos[1] -= point[1];
            const cplane = vec4.set(vec4.create(),cnorm[0],cnorm[1],cnorm[2],-vec3.dot(cpos,cnorm));
            const q = Mat4.mulVec4(vec4.create(),projection),[
                (cplane[0] > 0 ? 1 : -1),(cplane[1] > 0 ? 1 : -1),1,1]);
            const c = cplane.map(x => x * 2 / vec4.dot(cplane,q));
            projection[ 2] = c[0] - projection[ 3];
            projection[ 6] = c[1] - projection[ 7];
            projection[10] = c[2] - projection[11];
            projection[14] = c[3] - projection[15];
            return this;
        },copy(that)
        {
            this.setup(that.width,that.height,that.near,that.far,that.fov,that.aspect);
            Mat4.copy(view,that.view);
            Mat4.copy(projection,that.projection);
            return this;
        },setup(width = this.width,height = this.height,near = this.near,far = this.far,fov = this.fov,aspect = height / width)
        {
            this.width = width;
            this.height = height;
            this.near = near;
            this.far = far;
            this.fov = fov;
            this.aspect = aspect;
            
            const f = 1.0 / Math.tan(fov / 2);
            const range = 1.0 / (near - far);
            
            projection[0] = f * aspect
            projection[1] = 0.0;
            projection[2] = 0.0;
            projection[3] = 0.0;
            
            projection[4] = 0.0;
            projection[5] = f;
            projection[6] = 0.0;
            projection[7] = 0.0;
            
            projection[8] = 0.0;
            projection[9] = 0.0;
            projection[10] = (near + far) * range;
            projection[11] = -1.0;
            
            projection[12] = 0.0;
            projection[13] = 0.0;
            projection[14] = 2 * near * far * range;
            projection[15] = 0.0;
            return this;
        }
    }
};
const GameObject = (gl,mesh) =>
{
    const { vec3 } = glMatrix;
    return {
        pos:   [0,euler: [0,scale: [1,1],mesh,forward()
        {
            const matrix = Mat4.fromZRotation(Mat4.create(),this.euler[2]);
            Mat4.multiply(matrix,Mat4.fromXRotation(Mat4.create(),this.euler[0]));
            Mat4.multiply(matrix,this.euler[1]));
            return vec3.negate(vec3.create(),Mat4.zAxis(vec3.create(),matrix));
        },localToWorld()
        {
            const matrix = Mat4.fromTranslation(Mat4.create(),this.pos);
            Mat4.multiply(matrix,this.euler[1]));
            Mat4.multiply(matrix,Mat4.fromZRotation(Mat4.create(),this.euler[2]));
            Mat4.multiply(matrix,Mat4.fromScaling(Mat4.create(),this.scale));
            return matrix;
        },worldToLocal()
        {
            const matrix = Mat4.fromScaling(Mat4.create(),this.scale);
            Mat4.invert(matrix,matrix);
            Mat4.multiply(matrix,vec3.negate(vec3.create(),this.pos)));
            return matrix;
        },render(camera,fbo = null)
        {
            const mv = Mat4.transpose(Mat4.create(),this.worldToLocal());
            const mvp = Mat4.multiply(Mat4.create(),camera.matrix,this.localToWorld());
            this.mesh.render(mvp,mv);
            return this;
        },};
};

const Portal = (() =>
{
    const { vec3 } = glMatrix;
    const Warp = (fromPortal = {}) =>
    {
        const delta = Mat4.identity(Mat4.create());
        const deltaInv = Mat4.identity(Mat4.create());
        return {
            fromPortal,toPortal: null,delta,deltaInv,};
    };
    return {
        connectWarp(a,b)
        {
            a.toPortal = b.fromPortal;
            b.toPortal = a.toPortal;
            a.delta = Mat4.multiply(Mat4.create(),a.fromPortal.localToWorld(),b.fromPortal.worldToLocal());
            b.delta = Mat4.multiply(Mat4.create(),b.fromPortal.localToWorld(),a.fromPortal.worldToLocal());
            a.deltaInv = b.delta;
            b.deltaInv = a.delta;
            return this;
        },connect(a,b)
        {
            this.connectWarp(a.front,b.back);
            this.connectWarp(b.front,a.back);
            return this;
        },create: async (gl) =>
        {
            const mesh = await Mesh.load('double_quad.obj');
            const shader = await Shader(gl,'.','portal-shader');
            const { a_position } = shader.attribute;
            const vao = gl.createVertexArray();
            gl.bindVertexArray(vao);
            const buffers = createBuffers(gl,new Array(1));
            setupArrayBuffer(gl,buffers[0],new Float32Array(mesh.geometries[0].data.position),3,a_position);
            
            const portalCam = Camera();
            return (self =>
            {
                self.front = Warp(self);
                self.back  = Warp(self);
                return self;
            })({
                ...GameObject(gl,null),front: null,back:  null,get cam()
                {
                    return portalCam;
                },/*Override*/ render(camera,fbo,func)
                {
                    console.assert(this.euler[0] === 0);
                    console.assert(this.euler[2] === 0);
                    const normal = this.forward();
                    const camPos = Mat4.getTranslation(vec3.create(),camera.view));
                    const isFront = vec3.dot(vec3.subtract(vec3.create(),camPos,this.pos),normal) > 0;
                    
                    if (isFront)
                    {
                        vec3.negate(normal,normal);
                    }
                    const warp = isFront? this.front : this.back;
                    const mvp = Mat4.multiply(Mat4.create(),this.localToWorld());
                    
                    portalCam.copy(camera);
                    portalCam.clipOblique(vec3.add(vec3.create(),this.pos,normal.map(x => x * 0.1)),normal));
                    Mat4.multiply(portalCam.view,portalCam.view,warp.delta);
                    shader.use();
                    gl.bindVertexArray(vao);
                    
                    const { u_mvp,u_texture } = shader.uniform;
                    
                    gl.uniform1i(u_texture,0);
                    gl.activeTexture(gl.TEXTURE0 + 0);
                    fbo.use();
                    
                    gl.uniformMatrix4fv(u_mvp,false,mvp);
                    gl.drawArrays(gl.TRIANGLES,mesh.geometries[0].data.position.length / 3);
                    return this;
                },});
        },};
})();

起初,门户似乎正常,但是当我尝试在门户后面进行访问时,仍会渲染对象(如果我将自己放置在门户的摄像机位于它们后面的位置),但使用的是一种不寻常的方式。我不太擅长矩阵数学或线性代数,希望能对您有所帮助。谢谢。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)