Refractor constructor
Refractor(
- BufferGeometry? geometry, [
- Map<
String, dynamic> ? options
Implementation
Refractor(super.geometry, [Map<String,dynamic>? options] ) {
type = 'Refractor';
FlutterView view = WidgetsBinding.instance.platformDispatcher.views.first;
Size size = view.physicalSize;
width = size.width;
height = size.height;
//print("xxxx"+size.width.toString());
options ??= {};
final scope = this;
final color = THREE.Color.fromHex( options['color'] ?? 0x7F7F7F);
final textureWidth = options['textureWidth'] ?? 512;
final textureHeight = options['textureHeight'] ?? 512;
final clipBias = options['clipBias'] ?? 0;
final shader = options['shader'] ?? Reflector.reflectorShader;
final multisample = options['multisample'] ?? 4;
//
final virtualCamera = camera;
virtualCamera.matrixAutoUpdate = false;
virtualCamera.userData['refractor'] = true;
final refractorPlane = THREE.Plane();
final textureMatrix = THREE.Matrix4();
// render target
renderTarget = THREE.WebGLRenderTarget( textureWidth, textureHeight, THREE.WebGLRenderTargetOptions({'samples': multisample, 'type': THREE.HalfFloatType}));
// material
material = THREE.ShaderMaterial( {
'uniforms': THREE.UniformsUtils.clone( shader['uniforms'] ),
'vertexShader': shader['vertexShader'],
'fragmentShader': shader['fragmentShader'],
'transparent': true // ensures, refractors are drawn from farthest to closest
} );
material?.uniforms[ 'color' ]['value'] = color;
material?.uniforms[ 'tDiffuse' ]['value'] = renderTarget.texture;
material?.uniforms[ 'textureMatrix' ]['value'] = textureMatrix;
// functions
final visible = (() {
final refractorWorldPosition = THREE.Vector3();
final cameraWorldPosition = THREE.Vector3();
final rotationMatrix = THREE.Matrix4();
final view = THREE.Vector3();
final normal = THREE.Vector3();
return ( camera ) {
refractorWorldPosition.setFromMatrixPosition( scope.matrixWorld );
cameraWorldPosition.setFromMatrixPosition( camera.matrixWorld );
view.subVectors( refractorWorldPosition, cameraWorldPosition );
rotationMatrix.extractRotation( scope.matrixWorld );
normal.set( 0, 0, 1 );
normal.applyMatrix4( rotationMatrix );
return view.dot( normal ) < 0;
};
})();
final updateRefractorPlane = (() {
final normal = THREE.Vector3();
final position = THREE.Vector3();
final quaternion = THREE.Quaternion();
final scale = THREE.Vector3();
return() {
scope.matrixWorld.decompose( position, quaternion, scale );
normal.set( 0, 0, 1 ).applyQuaternion( quaternion ).normalize();
// flip the normal because we want to cull everything above the plane
normal.negate();
refractorPlane.setFromNormalAndCoplanarPoint( normal, position );
};
})();
final updateVirtualCamera = (() {
final clipPlane = THREE.Plane();
final clipVector = THREE.Vector4();
final q = THREE.Vector4();
return (THREE.Camera camera ) {
virtualCamera.matrixWorld.copy( camera.matrixWorld );
virtualCamera.matrixWorldInverse.copy( virtualCamera.matrixWorld ).invert();
virtualCamera.projectionMatrix.copy( camera.projectionMatrix );
virtualCamera.far = camera.far; // used in WebGLBackground
// The following code creates an oblique view frustum for clipping.
// see: Lengyel, Eric. “Oblique View Frustum Depth Projection and Clipping”.
// Journal of Game Development, Vol. 1, No. 2 (2005), Charles River Media, pp. 5–16
clipPlane.copy( refractorPlane );
clipPlane.applyMatrix4( virtualCamera.matrixWorldInverse );
clipVector.set( clipPlane.normal.x, clipPlane.normal.y, clipPlane.normal.z, clipPlane.constant );
// calculate the clip-space corner point opposite the clipping plane and
// transform it into camera space by multiplying it by the inverse of the projection matrix
final projectionMatrix = virtualCamera.projectionMatrix;
q.x = (clipVector.x.sign + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ];
q.y = (clipVector.y.sign + projectionMatrix.elements[ 9 ] ) / projectionMatrix.elements[ 5 ];
q.z = - 1.0;
q.w = ( 1.0 + projectionMatrix.elements[ 10 ] ) / projectionMatrix.elements[ 14 ];
// calculate the scaled plane vector
clipVector.multiplyScalar( 2.0 / clipVector.dot( q ) );
// replacing the third row of the projection matrix
projectionMatrix.elements[ 2 ] = clipVector.x.toDouble();
projectionMatrix.elements[ 6 ] = clipVector.y.toDouble();
projectionMatrix.elements[ 10 ] = clipVector.z.toDouble() + 1.0 - clipBias;
projectionMatrix.elements[ 14 ] = clipVector.w.toDouble();
};
} )();
// This will update the texture matrix that is used for projective texture mapping in the shader.
// see: http://developer.download.nvidia.com/assets/gamedev/docs/projective_texture_mapping.pdf
void updateTextureMatrix(THREE.Camera camera ) {
// this matrix does range mapping to [ 0, 1 ]
textureMatrix.set(
0.5, 0.0, 0.0, 0.5,
0.0, 0.5, 0.0, 0.5,
0.0, 0.0, 0.5, 0.5,
0.0, 0.0, 0.0, 1.0
);
// we use "Object Linear Texgen", so we need to multiply the texture matrix T
// (matrix above) with the projection and view matrix of the virtual camera
// and the model matrix of the refractor
textureMatrix.multiply( camera.projectionMatrix );
textureMatrix.multiply( camera.matrixWorldInverse );
textureMatrix.multiply( scope.matrixWorld );
}
//
void render(THREE.WebGLRenderer renderer, THREE.Object3D scene, THREE.Camera camera ) {
scope.visible = false;
final currentRenderTarget = renderer.getRenderTarget();
final currentXrEnabled = renderer.xr.enabled;
final currentShadowAutoUpdate = renderer.shadowMap.autoUpdate;
renderer.xr.enabled = false; // avoid camera modification
renderer.shadowMap.autoUpdate = false; // avoid re-computing shadows
renderer.setRenderTarget( renderTarget );
if ( renderer.autoClear == false ) renderer.clear();
renderer.render( scene, virtualCamera );
renderer.xr.enabled = currentXrEnabled;
renderer.shadowMap.autoUpdate = currentShadowAutoUpdate;
renderer.setRenderTarget( currentRenderTarget );
// restore viewport
// final viewport = camera.viewport;
//if ( viewport != null ) {
//why x 3?
renderer?.state.viewport(THREE.Vector4(0, 0,width, height));//OPENWORLD.Camera.width*3, OPENWORLD.Camera.height*3));// viewport );
// renderer.state.viewport( viewport );
//}
scope.visible = true;
}
//
onBeforeRender = ({
THREE.WebGLRenderer? renderer,
THREE.RenderTarget? renderTarget,
THREE.Object3D? mesh,
THREE.Scene? scene,
THREE.Camera? camera,
THREE.BufferGeometry? geometry,
THREE.Material? material,
Map<String, dynamic>? group
}){
// ensure refractors are rendered only once per frame
if ( camera?.userData['refractor'] == true ) return;
// avoid rendering when the refractor is viewed from behind
if ( !visible( camera ) == true ) return;
// update
updateRefractorPlane();
updateTextureMatrix( camera! );
updateVirtualCamera( camera );
render( renderer!, scene!, camera );
};
}