OpenGL_JS-blending_sorted 融合

211 阅读2分钟

image.png

混合多个物体

因为深度测试的原因,绘制两个半透明窗户,前一个窗户会将后一个窗户完全遮挡,也就是说后一个窗户重叠的部分被抛弃掉了

解决方法

1、先绘制所有不透明的物体。

2、对所有透明的物体排序。

3、按顺序绘制所有透明的物体

第二部的具体操作:
排序透明物体的一种方法是,从观察者视角获取物体的距离。这可以通过计算摄像机位置向量和物体的位置向量之间的距离所获得。接下来我们把距离和它对应的位置向量存储到一个STL库的map数据结构中。map会自动根据键值(Key)对它的值排序,所以只要我们添加了所有的位置,并以它的距离作为键,它们就会自动根据距离值排序了。

std::map<float, glm::vec3> sorted;
for (unsigned int i = 0; i < windows.size(); i++)
{
    float distance = glm::length(camera.Position - windows[i]);
    sorted[distance] = windows[i];
}

\

image.png

  • 顶点着色器
export let vs_blending =
`#version 300 es
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoords;
out vec2 TexCoords;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main()
{
    TexCoords = aTexCoords;
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}
`
  • 片元着色器
export let fs_blending =
`#version 300 es
precision mediump float;
out vec4 FragColor;
in vec2 TexCoords;
uniform sampler2D texture1;
void main()
{             
    FragColor = texture(texture1, TexCoords);
}
`
  • 渲染管线
    camera = new Camera(vec3.fromValues(0.0, 0.0, 10.0), vec3.fromValues(0.0, 1.0, 0.0));
    // 这三个
    gl.enable(gl.DEPTH_TEST);
    gl.enable(gl.BLEND);
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    shader = new Shader(gl, vs_blending, fs_blending);
    cubeTexture = loadTexture("../../textures/marble.jpg", 4, false);
    floorTexture = loadTexture("../../textures/metal.png", 4, false);
    transparentTexture = loadTexture("../../textures/window.png", 4, false);

    function loadTexture(url, nrComponents, gammaCorrection) {
        const textureID = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, textureID);
        const level = 0;
        const srcType = gl.UNSIGNED_BYTE;
        const pixel = new Uint8Array([0, 0, 255, 255]);
        gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, 1, 1, 0, gl.RGBA, srcType, pixel);
        const image = new Image();
        image.onload = function () {
            let internalFormat;
            let dataFormat;
            if (nrComponents == 1) {
                dataFormat = gl.RED;
                internalFormat = gl.R8;
            }
            else if (nrComponents == 3) {
                internalFormat = gammaCorrection ? gl.SRGB : gl.RGB;
                dataFormat = gl.RGB;
            }
            else if (nrComponents == 4) {
                internalFormat = gammaCorrection ? gl.SRGB8_ALPHA8 : gl.RGBA;
                dataFormat = gl.RGBA;
            }
            gl.bindTexture(gl.TEXTURE_2D, textureID);
            gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, image.naturalWidth, image.naturalHeight, 0, dataFormat, gl.UNSIGNED_BYTE, image);
            gl.generateMipmap(gl.TEXTURE_2D);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        };
        image.src = url;
        return textureID;
    }
    plane = new Quad();
    planeVao = CreateVAO(plane, { POSITION: 0, TEXCOORD_0: 1 });
    cube = new Cube();
    cubeVao = CreateVAO(cube, { POSITION: 0, TEXCOORD_0: 1 });
    transparent = new Quad();
    transparentVao = CreateVAO(transparent, { POSITION: 0, TEXCOORD_0: 1 });

    shader.use(gl);
    shader.setInt(gl, "texture1", 0);
    requestAnimationFrame(render);
    //
    function CreateVAO(geo, layout) {
        let VAO = gl.createVertexArray();
        gl.bindVertexArray(VAO);
        const vbo = gl.createBuffer();
        const ebo = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
        gl.bufferData(gl.ARRAY_BUFFER, geo.vertices, gl.STATIC_DRAW);
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebo);
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, geo.indices, gl.STATIC_DRAW);
        let acc = geo.accessors[geo.attributes.POSITION];
        gl.vertexAttribPointer(layout.POSITION, acc.countComponent, gl.FLOAT, false, acc.stride, acc.byteOffset);
        gl.enableVertexAttribArray(layout.POSITION);
        if (layout.TEXCOORD_0) {
            acc = geo.accessors[geo.attributes.TEXCOORD_0];
            gl.vertexAttribPointer(layout.TEXCOORD_0, acc.countComponent, gl.FLOAT, false, acc.stride, acc.byteOffset);
            gl.enableVertexAttribArray(layout.TEXCOORD_0);
        }
        if (layout.NORMAL) {
            acc = geo.accessors[geo.attributes.NORMAL];
            gl.vertexAttribPointer(layout.NORMAL, acc.countComponent, gl.FLOAT, false, acc.stride, acc.byteOffset);
            gl.enableVertexAttribArray(layout.NORMAL);
        }
        return VAO;
    }
  • 获取最近距离
windows =
    [
        vec3.fromValues(-2.0, 0.0, -0.48),
        vec3.fromValues(3.0, 0.0, 0.51),
        vec3.fromValues(0.0, 0.0, 2.7),
        vec3.fromValues(-0.6, 0.0, -2.3),
        vec3.fromValues(1.0, 0.0, -0.6)
    ];
    let currentFrame = performance.now() / 1000;
    deltaTime = (currentFrame - lastFrame) * 1000;
    lastFrame = currentFrame;
    processInput();
    let sortArray = [];
    let sub = vec3.create();
    for (let i = 0; i < windows.length; i++) {
        let distance = vec3.len(vec3.subtract(sub, camera.Position, windows[i]));
        sortArray.push([distance, windows[i]]);
    }
    sortArray.sort(function (a, b) {
        return b[0] - a[0];
    });
    gl.clearColor(0.1, 0.1, 0.1, 1.0);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  • drawcall cube
   shader.use(gl);
   let projection = mat4.create();
   mat4.perspective(projection, (camera.Zoom) * Math.PI / 180, canvas.width / canvas.height, 0.1, 100.0);
   gl.uniformMatrix4fv(gl.getUniformLocation(shader.programId, "projection"), false, projection);
   let view = camera.GetViewMatrix();
   gl.uniformMatrix4fv(gl.getUniformLocation(shader.programId, "view"), false, view);
   let model = mat4.create();
   mat4.identity(model);
   gl.bindVertexArray(cubeVao);
   gl.activeTexture(gl.TEXTURE0);
   gl.bindTexture(gl.TEXTURE_2D, cubeTexture);
   mat4.translate(model, model, vec3.fromValues(-2.0, 0.0, -1.0));
   gl.uniformMatrix4fv(gl.getUniformLocation(shader.programId, "model"), false, model);
   gl.drawElements(gl.TRIANGLES, cube.indices.length, gl.UNSIGNED_SHORT, 0);
   mat4.identity(model);
   mat4.translate(model, model, vec3.fromValues(3.0, 0.0, 0.0));
   gl.uniformMatrix4fv(gl.getUniformLocation(shader.programId, "model"), false, model);
   gl.drawElements(gl.TRIANGLES, cube.indices.length, gl.UNSIGNED_SHORT, 0);
  • drawcall plane
    gl.bindVertexArray(planeVao);
    gl.bindTexture(gl.TEXTURE_2D, floorTexture);
    mat4.identity(model);
    mat4.fromXRotation(model, Math.PI / 2);
    mat4.scale(model, model, [5.0, 5.0, 1.0]);
    gl.uniformMatrix4fv(gl.getUniformLocation(shader.programId, "model"), false, model);
    gl.drawElements(gl.TRIANGLES, plane.indices.length, gl.UNSIGNED_SHORT, 0);
  • drawcall 透明物体 从远到近draw 先drawcall 不透明
    gl.bindVertexArray(transparentVao);
    gl.bindTexture(gl.TEXTURE_2D, transparentTexture);
    for (let i = 0, iLen = sortArray.length; i < iLen; i++) {
        model = mat4.identity(model);
        mat4.translate(model, model, sortArray[i][1]);
        gl.uniformMatrix4fv(gl.getUniformLocation(shader.programId, "model"), false, model);
        gl.drawElements(gl.TRIANGLES, transparent.indices.length, gl.UNSIGNED_SHORT, 0);
    }
    requestAnimationFrame(render);
}

;

//# sourceMappingURL=blending_sorted.js.map