如何在 webgl 中正确设置透明度
How to set Transparency properly in webgl
我是图形和 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);
渲染每个部分后,我将使 depthMask 为真。
下面是需要的渲染图
下面是我在 webgl 中得到的实际渲染图像。
我没有使用像 threejs 这样的任何 webgl 库。
请帮我解决这个问题。
在 OpenGL 或 WebGL 中没有自动获得深度正确透明度的方法。多年来,已使用多种方法来实现此目的:
深度排序:由于绘图是按照 draw-calls 提交的顺序进行的,因此所有对象都按相机深度排序。如果您不仅关心多个对象,还关心同一对象的 front/back 个面的正确透明度,您还必须根据三角形的深度对对象中的三角形进行排序。实现此目的的最佳方法是使用 bsp tree.
另一个选项是Order Independent Transparency 字段的算法,例如Depth Peeling or Per-Pixel Linked Lists。虽然我不确定这些方法中哪些可以在WebGL中实现。
对于您的示例,它可以像关闭深度测试一样简单。
gl.disable(gl.DEPTH_TEST);
问题在于深度测试,如果在前面绘制某些东西,深度测试将阻止绘制后面的任何东西。
另一种解决方案是按距相机的 z 距离对对象进行排序,然后从后向前绘制,但这只是针对您的情况的部分解决方案,因为即使绘制单个立方体,您也需要确保先绘制后面的面前面的面孔。在那种情况下,您首先必须将所有对象从前到后排序,然后将每个对象绘制两次,一次剔除正面三角形,然后再次剔除背面三角形。这仅适用于凸物体。
过去你必须开始细分对象或使用其他技术,如他的回答中引用的 BDL。
在你的情况下,因为一切都是透明的,看起来你可以一起关闭深度测试。
虽然
你可能想要use premultiplied 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>
我是图形和 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);
渲染每个部分后,我将使 depthMask 为真。
下面是需要的渲染图
下面是我在 webgl 中得到的实际渲染图像。
我没有使用像 threejs 这样的任何 webgl 库。
请帮我解决这个问题。
在 OpenGL 或 WebGL 中没有自动获得深度正确透明度的方法。多年来,已使用多种方法来实现此目的:
深度排序:由于绘图是按照 draw-calls 提交的顺序进行的,因此所有对象都按相机深度排序。如果您不仅关心多个对象,还关心同一对象的 front/back 个面的正确透明度,您还必须根据三角形的深度对对象中的三角形进行排序。实现此目的的最佳方法是使用 bsp tree.
另一个选项是Order Independent Transparency 字段的算法,例如Depth Peeling or Per-Pixel Linked Lists。虽然我不确定这些方法中哪些可以在WebGL中实现。
对于您的示例,它可以像关闭深度测试一样简单。
gl.disable(gl.DEPTH_TEST);
问题在于深度测试,如果在前面绘制某些东西,深度测试将阻止绘制后面的任何东西。
另一种解决方案是按距相机的 z 距离对对象进行排序,然后从后向前绘制,但这只是针对您的情况的部分解决方案,因为即使绘制单个立方体,您也需要确保先绘制后面的面前面的面孔。在那种情况下,您首先必须将所有对象从前到后排序,然后将每个对象绘制两次,一次剔除正面三角形,然后再次剔除背面三角形。这仅适用于凸物体。
过去你必须开始细分对象或使用其他技术,如他的回答中引用的 BDL。
在你的情况下,因为一切都是透明的,看起来你可以一起关闭深度测试。
虽然
你可能想要use premultiplied 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>