三个带有透明度的js后处理解决方案
Three js postProcessing solution with transparency
我发现了使用 three.js 创建后处理效果的解决方案:
https://medium.com/@luruke/simple-postprocessing-in-three-js-91936ecadfb7
(由 Luigi De Rosa 制作)
这是一个很好的方法。不幸的是,我无法在最终渲染器中添加透明度。我应该在我的后处理片段着色器中添加一个透明组件吗?
const fragmentShader = `precision highp float;
uniform sampler2D uScene;
uniform vec2 uResolution;
uniform float uTime;
void main() {
vec2 uv = gl_FragCoord.xy / uResolution.xy;
vec3 color = vec3(uv, 1.0);
//simple distortion effect
uv.y += sin(uv.x*30.0+uTime*10.0)/40.0;
uv.x -= sin(uv.y*10.0-uTime)/40.0;
color = texture2D(uScene, uv).rgb;
gl_FragColor = vec4(color, 1.0);
}
`;
谢谢
编辑 1:
我将属性 transparent:true
添加到 RawShaderMaterial
。
我将新 THREE.WebGLRenderTarget
的格式更改为 THREE.RGBAFormat
而不是 THREE.RGBFormat
。
我还在片段着色器的末尾添加了这些行:
gl_FragColor = vec4(color, 1.0);
vec4 tex = texture2D( uScene, uv );
if(tex.a < 0.0) {
gl_FragColor.a = 1.0;
}
可是我还是没看透我的canvas
编辑 2:
这是后处理的片段 class
let renderer, camera, scene, W = window.innerWidth, H = window.innerHeight, geometry, material, mesh;
initWebgl();
function initWebgl(){
renderer = new THREE.WebGLRenderer( { alpha: true, antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( W, H );
document.querySelector('.innerCanvas').appendChild( renderer.domElement );
camera = new THREE.OrthographicCamera(-W/H/2, W/H/2, 1/2, -1/2, -0.1, 0.1);
scene = new THREE.Scene();
geometry = new THREE.PlaneBufferGeometry(0.5, 0.5);
material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh( geometry , material );
scene.add(mesh);
}
function rafP(){
requestAnimationFrame(rafP);
// renderer.render(scene, camera);
post.render(scene, camera);
}
const vertexShader = `precision highp float;
attribute vec2 position;
void main() {
// Look ma! no projection matrix multiplication,
// because we pass the values directly in clip space coordinates.
gl_Position = vec4(position, 1.0, 1.0);
}`;
const fragmentShader = `precision highp float;
uniform sampler2D uScene;
uniform vec2 uResolution;
uniform float uTime;
void main() {
vec2 uv = gl_FragCoord.xy / uResolution.xy;
vec3 color = vec3(uv, 1.0);
uv.y += sin(uv.x*20.0)/10.0;
color = texture2D(uScene, uv).rgb;
gl_FragColor = vec4(color, 1.0);
vec4 tex = texture2D( uScene, uv );
// if(tex.a - percent < 0.0) {
if(tex.a < 0.0) {
gl_FragColor.a = 1.0;
//or without transparent = true use
// discard;
}
}`;
//PostProcessing
class PostFX {
constructor(renderer) {
this.renderer = renderer;
this.scene = new THREE.Scene();
// three.js for .render() wants a camera, even if we're not using it :(
this.dummyCamera = new THREE.OrthographicCamera();
this.geometry = new THREE.BufferGeometry();
// Triangle expressed in clip space coordinates
const vertices = new Float32Array([
-1.0, -1.0,
3.0, -1.0,
-1.0, 3.0
]);
this.geometry.addAttribute('position', new THREE.BufferAttribute(vertices, 2));
this.resolution = new THREE.Vector2();
this.renderer.getDrawingBufferSize(this.resolution);
this.target = new THREE.WebGLRenderTarget(this.resolution.x, this.resolution.y, {
format: THREE.RGBAFormat, //THREE.RGBFormat
stencilBuffer: false,
depthBuffer: true
});
this.material = new THREE.RawShaderMaterial({
fragmentShader,
vertexShader,
uniforms: {
uScene: { value: this.target.texture },
uResolution: { value: this.resolution }
},
transparent:true
});
// TODO: handle the resize -> update uResolution uniform and this.target.setSize()
this.triangle = new THREE.Mesh(this.geometry, this.material);
// Our triangle will be always on screen, so avoid frustum culling checking
this.triangle.frustumCulled = false;
this.scene.add(this.triangle);
}
render(scene, camera) {
this.renderer.setRenderTarget(this.target);
this.renderer.render(scene, camera);
this.renderer.setRenderTarget(null);
this.renderer.render(this.scene, this.dummyCamera);
console.log(this.renderer);
}
}
post = new PostFX(renderer);
rafP();
body{
margin:0;
padding:0;
background:#00F;
}
.innerCanvas{
position:fixed;
top:0;
left:0;
width:100%;
height:100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/105/three.js"></script>
<div class="innerCanvas"></div>
在 Alpha 通道上,0 表示完全透明,1 表示完全不透明。
在这种情况下,您唯一需要做的就是传递 gl_FragColor
纹理样本的结果。你甚至不需要担心它的价值。
gl_FragColor = texture2D(uScene, uv);
我发现了使用 three.js 创建后处理效果的解决方案:
https://medium.com/@luruke/simple-postprocessing-in-three-js-91936ecadfb7
(由 Luigi De Rosa 制作)
这是一个很好的方法。不幸的是,我无法在最终渲染器中添加透明度。我应该在我的后处理片段着色器中添加一个透明组件吗?
const fragmentShader = `precision highp float;
uniform sampler2D uScene;
uniform vec2 uResolution;
uniform float uTime;
void main() {
vec2 uv = gl_FragCoord.xy / uResolution.xy;
vec3 color = vec3(uv, 1.0);
//simple distortion effect
uv.y += sin(uv.x*30.0+uTime*10.0)/40.0;
uv.x -= sin(uv.y*10.0-uTime)/40.0;
color = texture2D(uScene, uv).rgb;
gl_FragColor = vec4(color, 1.0);
}
`;
谢谢
编辑 1:
我将属性 transparent:true
添加到 RawShaderMaterial
。
我将新 THREE.WebGLRenderTarget
的格式更改为 THREE.RGBAFormat
而不是 THREE.RGBFormat
。
我还在片段着色器的末尾添加了这些行:
gl_FragColor = vec4(color, 1.0);
vec4 tex = texture2D( uScene, uv );
if(tex.a < 0.0) {
gl_FragColor.a = 1.0;
}
可是我还是没看透我的canvas
编辑 2:
这是后处理的片段 class
let renderer, camera, scene, W = window.innerWidth, H = window.innerHeight, geometry, material, mesh;
initWebgl();
function initWebgl(){
renderer = new THREE.WebGLRenderer( { alpha: true, antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( W, H );
document.querySelector('.innerCanvas').appendChild( renderer.domElement );
camera = new THREE.OrthographicCamera(-W/H/2, W/H/2, 1/2, -1/2, -0.1, 0.1);
scene = new THREE.Scene();
geometry = new THREE.PlaneBufferGeometry(0.5, 0.5);
material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh( geometry , material );
scene.add(mesh);
}
function rafP(){
requestAnimationFrame(rafP);
// renderer.render(scene, camera);
post.render(scene, camera);
}
const vertexShader = `precision highp float;
attribute vec2 position;
void main() {
// Look ma! no projection matrix multiplication,
// because we pass the values directly in clip space coordinates.
gl_Position = vec4(position, 1.0, 1.0);
}`;
const fragmentShader = `precision highp float;
uniform sampler2D uScene;
uniform vec2 uResolution;
uniform float uTime;
void main() {
vec2 uv = gl_FragCoord.xy / uResolution.xy;
vec3 color = vec3(uv, 1.0);
uv.y += sin(uv.x*20.0)/10.0;
color = texture2D(uScene, uv).rgb;
gl_FragColor = vec4(color, 1.0);
vec4 tex = texture2D( uScene, uv );
// if(tex.a - percent < 0.0) {
if(tex.a < 0.0) {
gl_FragColor.a = 1.0;
//or without transparent = true use
// discard;
}
}`;
//PostProcessing
class PostFX {
constructor(renderer) {
this.renderer = renderer;
this.scene = new THREE.Scene();
// three.js for .render() wants a camera, even if we're not using it :(
this.dummyCamera = new THREE.OrthographicCamera();
this.geometry = new THREE.BufferGeometry();
// Triangle expressed in clip space coordinates
const vertices = new Float32Array([
-1.0, -1.0,
3.0, -1.0,
-1.0, 3.0
]);
this.geometry.addAttribute('position', new THREE.BufferAttribute(vertices, 2));
this.resolution = new THREE.Vector2();
this.renderer.getDrawingBufferSize(this.resolution);
this.target = new THREE.WebGLRenderTarget(this.resolution.x, this.resolution.y, {
format: THREE.RGBAFormat, //THREE.RGBFormat
stencilBuffer: false,
depthBuffer: true
});
this.material = new THREE.RawShaderMaterial({
fragmentShader,
vertexShader,
uniforms: {
uScene: { value: this.target.texture },
uResolution: { value: this.resolution }
},
transparent:true
});
// TODO: handle the resize -> update uResolution uniform and this.target.setSize()
this.triangle = new THREE.Mesh(this.geometry, this.material);
// Our triangle will be always on screen, so avoid frustum culling checking
this.triangle.frustumCulled = false;
this.scene.add(this.triangle);
}
render(scene, camera) {
this.renderer.setRenderTarget(this.target);
this.renderer.render(scene, camera);
this.renderer.setRenderTarget(null);
this.renderer.render(this.scene, this.dummyCamera);
console.log(this.renderer);
}
}
post = new PostFX(renderer);
rafP();
body{
margin:0;
padding:0;
background:#00F;
}
.innerCanvas{
position:fixed;
top:0;
left:0;
width:100%;
height:100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/105/three.js"></script>
<div class="innerCanvas"></div>
在 Alpha 通道上,0 表示完全透明,1 表示完全不透明。
在这种情况下,您唯一需要做的就是传递 gl_FragColor
纹理样本的结果。你甚至不需要担心它的价值。
gl_FragColor = texture2D(uScene, uv);