如何在WebGL中正确设置透明度

3

我是一个新手,对图形和WebGL不太熟悉。在WebGL 1.0中设置模型的透明度时遇到了问题。

模型包含多个部分(几何形状)。

着色器代码如下:

"if (usetransparency > 0.0) {\n" +
        "gl_FragColor = vec4(( diffuse - 0.2) * diffColor, 1.0); \n" +
        "gl_FragColor.w = transparency;  \n" +
    "}  \n" 

Js代码是

 shader.setUseTransparency(1.0);
 shader.setTransparency(transparencyValue);
 GL.clearColor(0.5, 0.5, 0.5, 0.0);
 GL.enable(GL.DEPTH_TEST);
 GL.depthFunc(GL.LEQUAL)
 GL.depthMask(false);
 GL.enable(GL.BLEND);
 GL.blendFunc(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA);

在每个部分渲染完成后,我将使深度蒙版为true。
下面是所需的渲染图像: enter image description here 以下是我在WebGL中实际得到的渲染图像。 enter image description here 我没有使用像threejs这样的WebGL库。
请帮忙解决这个问题。

也许这可以帮助:OpenGL透明度的廉价技巧 - deblocker
2个回答

4

在OpenGL或WebGL中,没有自动获取深度正确透明度的方法。多年来,已经使用了多种方法来实现这一点:

深度排序:由于绘制是按提交的绘图调用的顺序发生的,因此将所有对象按照它们的相机深度进行排序。如果您不仅关心多个对象,而且还关心同一对象的前/后面面的正确透明度,则还必须根据其深度对对象中的三角形进行排序。实现这一点的最佳方法是使用BSP树

另一个选择是Order Independent Transparency领域中的算法,例如Depth PeelingPer-Pixel Linked Lists。尽管我不确定这些方法中哪些可以在WebGL中实现。


2

对于您的示例,可以简单地关闭深度测试。

gl.disable(gl.DEPTH_TEST);

问题出在深度测试上,如果某个物体被绘制在前面,深度测试将防止后面的任何物体被绘制。

另一个解决方案是按照相机与物体之间的z距离对对象进行排序,并从后往前绘制,但这只是你的情况的部分解决方案,因为即使绘制单个立方体,你也需要确保后面的面在前面的面之前绘制。在这种情况下,你首先必须将所有对象从前到后排序,然后每个对象都要绘制两次,一次使用正面三角形剔除,然后再使用背面三角形剔除。这仅适用于凸对象。

除此之外,你必须开始细分对象或使用其他技术,如BDL中提到的。

不过,在你的情况下,由于所有东西都是透明的,看起来你可以完全关闭深度测试。

不过,你可能想要使用预乘alpha

"use strict";

const vs = `
uniform mat4 u_worldViewProjection;
uniform vec3 u_lightWorldPos;
uniform mat4 u_world;
uniform mat4 u_viewInverse;
uniform mat4 u_worldInverseTranspose;

attribute vec4 a_position;
attribute vec3 a_normal;
attribute vec2 a_texcoord;

varying vec4 v_position;
varying vec2 v_texCoord;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;

void main() {
  v_texCoord = a_texcoord;
  v_position = (u_worldViewProjection * a_position);
  v_normal = (u_worldInverseTranspose * vec4(a_normal, 0)).xyz;
  v_surfaceToLight = u_lightWorldPos - (u_world * a_position).xyz;
  gl_Position = v_position;
}
`;
const fs = `
precision mediump float;

varying vec4 v_position;
varying vec2 v_texCoord;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;

uniform vec4 u_lightColor;
uniform vec4 u_diffuseMult;
uniform sampler2D u_diffuse;

void main() {
  vec4 diffuseColor = texture2D(u_diffuse, v_texCoord) * u_diffuseMult;
  vec3 a_normal = normalize(v_normal);
  vec3 surfaceToLight = normalize(v_surfaceToLight);
  float lit = abs(dot(a_normal, surfaceToLight));
  gl_FragColor = vec4(diffuseColor.rgb * lit, diffuseColor.a);
  gl_FragColor.rgb *= gl_FragColor.a;
}
`;


twgl.setDefaults({attribPrefix: "a_"});
var m4 = twgl.m4;
var gl = document.querySelector("canvas").getContext("webgl");
var programInfo = twgl.createProgramInfo(gl, [vs, fs]);

var shapes = [
  twgl.primitives.createCubeBufferInfo(gl, 2),
  twgl.primitives.createSphereBufferInfo(gl, 1, 24, 12),
  twgl.primitives.createPlaneBufferInfo(gl, 2, 2),
  twgl.primitives.createTruncatedConeBufferInfo(gl, 1, 0, 2, 24, 1),
  twgl.primitives.createCresentBufferInfo(gl, 1, 1, 0.5, 0.1, 24),
  twgl.primitives.createCylinderBufferInfo(gl, 1, 2, 24, 2),
  twgl.primitives.createDiscBufferInfo(gl, 1, 24),
  twgl.primitives.createTorusBufferInfo(gl, 1, 0.4, 24, 12),
];

function rand(min, max) {
  return min + Math.random() * (max - min);
}

  // Shared values
var lightWorldPosition = [1, 8, -10];
var lightColor = [1, 1, 1, 0.2];
var camera = m4.identity();
var view = m4.identity();
var viewProjection = m4.identity();

var tex = twgl.createTexture(gl, {
  min: gl.NEAREST,
  mag: gl.NEAREST,
  src: [
    255, 255, 255, 255,
    192, 192, 192, 255,
    192, 192, 192, 255,
    255, 255, 255, 255,
  ],
    });

    var objects = [];
  var drawObjects = [];
  var numObjects = 100;
  var baseHue = rand(0, 360);
  for (var ii = 0; ii < numObjects; ++ii) {
  var uniforms = {
  u_lightWorldPos: lightWorldPosition,
  u_lightColor: lightColor,
  u_diffuseMult: [rand(.5, 1.), rand(.5, 1), rand(.5, 1), .5],
  u_diffuse: tex,
  u_viewInverse: camera,
  u_world: m4.identity(),
  u_worldInverseTranspose: m4.identity(),
  u_worldViewProjection: m4.identity(),
};
                             drawObjects.push({
                             programInfo: programInfo,
                             bufferInfo: shapes[ii % shapes.length],
                             uniforms: uniforms,
                             });
objects.push({
  translation: [rand(-10, 10), rand(-10, 10), rand(-10, 10)],
  ySpeed: rand(0.1, 0.3),
  zSpeed: rand(0.1, 0.3),
  uniforms: uniforms,
});
}

function render(time) {
  time *= 0.0001;
  twgl.resizeCanvasToDisplaySize(gl.canvas);
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

  gl.disable(gl.DEPTH_TEST);
  gl.enable(gl.BLEND);
  gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);


  var projection = m4.perspective(30 * Math.PI / 180, gl.canvas.clientWidth / gl.canvas.clientHeight, 0.5, 100);
  var eye = [1, 4, -20];
  var target = [0, 0, 0];
  var up = [0, 1, 0];

  m4.lookAt(eye, target, up, camera);
  m4.inverse(camera, view);
  m4.multiply(projection, view, viewProjection);

  objects.forEach(function(obj) {
    var uni = obj.uniforms;
    var world = uni.u_world;
    m4.identity(world);
    m4.rotateY(world, time * obj.ySpeed, world);
    m4.rotateZ(world, time * obj.zSpeed, world);
    m4.translate(world, obj.translation, world);
    m4.rotateX(world, time, world);
    m4.transpose(m4.inverse(world, uni.u_worldInverseTranspose), uni.u_worldInverseTranspose);
    m4.multiply(viewProjection, uni.u_world, uni.u_worldViewProjection);
  });

  twgl.drawObjectList(gl, drawObjects);

  requestAnimationFrame(render);
}
requestAnimationFrame(render);
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://twgljs.org/dist/2.x/twgl-full.min.js"></script>
<canvas></canvas>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接