我想基于计算着色器内的先前像素位置来实现每个对象的运动模糊效果。
这项技术的第一步是建立移动物体的速度图。这一步要求是将当前帧的投影和模型视图矩阵与前一帧的相同矩阵作为统一变量。
如何为某些特殊着色器将这些矩阵包含在制服中?我应该以某种方式解决问题:
uniforms = {
some_uniform_var : {type: "m4", value: initialMatrix, getter: function(){
// `this` points to object
return this.worldMatrix
}}
}
但现在在THREE.js中,这是不可用的。我们可以做一些猴子修补,但我找不到最好的方法来做到这一点。
有什么建议吗?
答案 0 :(得分:0)
目前对这个问题的解决方案包括几个部分。我使用EffectComposer进行渲染场景的多次传递,其中一个是VelocityPass。它需要当前和之前的模型 - 视图矩阵和投影矩阵,并产生两个位置。然后它们都用来计算一个点的速度。
Shader看起来像这样
"void main() {",
"vec2 a = (pos.xy / pos.w) * 0.5 + 0.5;",
"vec2 b = (prevPos.xy / prevPos.w) * 0.5 + 0.5;",
"vec2 oVelocity = a - b;",
"gl_FragColor = vec4(oVelocity, 0.0, 1.);",
"}"
这个决定有几个问题。
Three.js具有将对象相关着色器注入矩阵的特定点。 SetProgram
关闭的终点,它位于WebGLRenderer
。这就是我采用整个渲染器文件,将渲染器重命名为THREE.MySuperDuperWebGLRenderer
并在其中添加几行代码的原因:
用于访问闭包的闭包,在用户空间中定义:
function materialPerObjectSetup(material, object){
if( material.customUniformUpdate ){
material.customUniformUpdate( object, material, _gl ); // Yes, I had to pass it...
}
}
在renderBuffer
和renderBufferDirect
;
var program = setProgram( camera, lights, fog, material, object );
materialPerObjectSetup(material, object);
现在 - 用户空间部分:
velocityMat = new THREE.ShaderMaterial( THREE.VelocityShader );
velocityMat.customUniformUpdate = function(obj, mat, _gl){
// console.log("gotcha");
var new_m = obj.matrixWorld;
var p_uniforms = mat.program.uniforms;
var mvMatrix = camera.matrixWorldInverse.clone().multiplyMatrices(camera.matrixWorldInverse, obj._oldMatrix );
_gl.uniformMatrix4fv( p_uniforms.prevModelViewMatrix, false, mvMatrix.elements );
_gl.uniformMatrix4fv( p_uniforms.prevProjectionMatrix, false, camera.projectionMatrix.elements );
obj._pass_complete = true; // Необходимо сохранять состояние старой матрицы пока не отрисуется этот пасс.
// А то матрицы обновляются каждый рендеринг сцены.
}
当我们多次重新渲染场景时需要 _pass_complete
- 每次重新计算矩阵。这个技巧可以帮助我们保存以前的矩阵,直到我们使用它。
_gl.uniformMatrix4fv
是必需的,因为three.js在渲染之前一次为Universe提供服务。无论我们有多少对象 - 其他方法都会传递给最后一个的着色器modelViewMatrix
。这是因为我想使用VelocityShader
完全绘制这个场景。没有其他方法可以告诉Renderer使用一些替代材料作为对象。
作为这个解释的最后一点,我在这里设置一个技巧来管理对象的前一个矩阵:
THREE.Mesh.prototype._updateMatrixWorld = rotatedObject.updateMatrixWorld;
THREE.Mesh.prototype._pass_complete = true;
Object.defineProperty(THREE.Mesh.prototype, "updateMatrixWorld", {get: function(){
if(this._pass_complete){
this._oldMatrix = this.matrixWorld.clone();
this._pass_complete = false;
}
this._updateMatrixWorld();
return (function(){
});
}})
我相信,这可能是一个更好的解决方案。但有时我需要匆忙行事。而这种猴子的事情可能会发生。