| - [CustomBumpMapPlugin](#custombumpmapplugin) - Custom Bump Map material extension for PhysicalMaterial | - [CustomBumpMapPlugin](#custombumpmapplugin) - Custom Bump Map material extension for PhysicalMaterial | ||||
| - [ClearcoatTintPlugin](#clearcoattintplugin) - Clearcoat Tint material extension for PhysicalMaterial | - [ClearcoatTintPlugin](#clearcoattintplugin) - Clearcoat Tint material extension for PhysicalMaterial | ||||
| - [FragmentClippingExtensionPlugin](#fragmentclippingextensionplugin) - Fragment/SDF Clipping material extension for PhysicalMaterial | - [FragmentClippingExtensionPlugin](#fragmentclippingextensionplugin) - Fragment/SDF Clipping material extension for PhysicalMaterial | ||||
| - [ParallaxMappingPlugin](#parallaxmappingplugin) - Relief Parallax Bump Mapping extension for PhysicalMaterial | |||||
| - [HDRiGroundPlugin](#hdrigroundplugin) - Add support for ground projected hdri/skybox to the webgl background shader. | - [HDRiGroundPlugin](#hdrigroundplugin) - Add support for ground projected hdri/skybox to the webgl background shader. | ||||
| - [VirtualCamerasPlugin](#virtualcamerasplugin) - Add support for rendering virtual cameras before the main one every frame. | - [VirtualCamerasPlugin](#virtualcamerasplugin) - Add support for rendering virtual cameras before the main one every frame. | ||||
| - [EditorViewWidgetPlugin](#editorviewwidgetplugin) - Adds an interactive ViewHelper/AxisHelper that syncs with the main camera. | - [EditorViewWidgetPlugin](#editorviewwidgetplugin) - Adds an interactive ViewHelper/AxisHelper that syncs with the main camera. | ||||
| material.setDirty() | material.setDirty() | ||||
| ``` | ``` | ||||
| ## ParallaxMappingPlugin | |||||
| [//]: # (todo: image) | |||||
| [Example](https://threepipe.org/examples/#parallax-mapping-plugin/) — | |||||
| [Source Code](./src/plugins/material/ParallaxMappingPlugin.ts) — | |||||
| [API Reference](https://threepipe.org/docs/classes/ParallaxMappingPlugin.html) | |||||
| `ParallaxMappingPlugin` adds a material extension to PhysicalMaterial to add support for [parallax relief mapping](https://en.wikipedia.org/wiki/Relief_mapping_(computer_graphics)). The idea is to walk along a ray that has entered the bumpmap's volume, finding the intersection point of the ray with the bumpmap. [Steep parallax mapping](https://en.wikipedia.org/wiki/Parallax_mapping) and [parallax occlusion mapping](https://en.wikipedia.org/wiki/Parallax_occlusion_mapping) are other common names for these techniques. | |||||
| To use the plugin, add the plugin to the viewer and use the `bumpMap` in `PhysicalMaterial` normally. The max height is determined by the `bumpScale` in the material. This is assumed to be in world scale. | |||||
| ```typescript | |||||
| import {ThreeViewer, ParallaxMappingPlugin} from 'threepipe' | |||||
| const viewer = new ThreeViewer({...}) | |||||
| const parallaxMapping = viewer.addPluginSync(ParallaxMappingPlugin) | |||||
| // load or create an object | |||||
| // set the bump map | |||||
| object.material.bumpMap = await viewer.load<ITexture>(bumps[0]) || null | |||||
| // set the bump scale | |||||
| object.material.bumpScale = 0.1 | |||||
| // setDirty to notify the viewer to update. | |||||
| object.material.setDirty() | |||||
| ``` | |||||
| ### References and related links: | |||||
| - WebGL implementation by Rabbid76 - [github.com/Rabbid76/graphics-snippets](https://github.com/Rabbid76/graphics-snippets/blob/master/html/technique/parallax_005_parallax_relief_mapping_derivative_tbn.html) | |||||
| - Lesson on Parallax Occlusion Mapping in GLSL - [http://sunandblackcat.com/tipFullView.php?topicid=28](https://web.archive.org/web/20190128023901/http://sunandblackcat.com/tipFullView.php?topicid=28) | |||||
| - Learn OpenGL - https://learnopengl.com/Advanced-Lighting/Parallax-Mapping | |||||
| ## HDRiGroundPlugin | ## HDRiGroundPlugin | ||||
| [//]: # (todo: image) | [//]: # (todo: image) |
| <!DOCTYPE html> | |||||
| <html lang="en"> | |||||
| <head> | |||||
| <meta charset="UTF-8"> | |||||
| <title>Parallax Bump Mapping (Relief)</title> | |||||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |||||
| <!-- Import maps polyfill --> | |||||
| <!-- Remove this when import maps will be widely supported --> | |||||
| <script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script> | |||||
| <script type="importmap"> | |||||
| { | |||||
| "imports": { | |||||
| "threepipe": "./../../dist/index.mjs", | |||||
| "@threepipe/plugin-tweakpane": "./../../plugins/tweakpane/dist/index.mjs" | |||||
| } | |||||
| } | |||||
| </script> | |||||
| <style id="example-style"> | |||||
| html, body, #canvas-container, #mcanvas { | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| margin: 0; | |||||
| overflow: hidden; | |||||
| } | |||||
| </style> | |||||
| <script type="module" src="../examples-utils/simple-code-preview.mjs"></script> | |||||
| <script id="example-script" type="module" src="./script.js" data-scripts="./script.ts;./script.js"></script> | |||||
| </head> | |||||
| <body> | |||||
| <div id="canvas-container"> | |||||
| <canvas id="mcanvas"></canvas> | |||||
| </div> | |||||
| </body> |
| import { | |||||
| _testFinish, | |||||
| BoxGeometry, | |||||
| ITexture, | |||||
| Mesh, | |||||
| ParallaxMappingPlugin, | |||||
| PhysicalMaterial, | |||||
| SSAAPlugin, | |||||
| ThreeViewer, | |||||
| } from 'threepipe' | |||||
| import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane' | |||||
| async function init() { | |||||
| const viewer = new ThreeViewer({ | |||||
| canvas: document.getElementById('mcanvas') as HTMLCanvasElement, | |||||
| msaa: true, | |||||
| plugins: [new SSAAPlugin(4)], | |||||
| dropzone: { | |||||
| addOptions: { | |||||
| disposeSceneObjects: true, | |||||
| }, | |||||
| }, | |||||
| }) | |||||
| const parallaxMapping = viewer.addPluginSync(ParallaxMappingPlugin) | |||||
| console.log(parallaxMapping) | |||||
| const ui = viewer.addPluginSync(new TweakpaneUiPlugin(true)) | |||||
| ui.setupPluginUi(ParallaxMappingPlugin, {expanded: true}) | |||||
| await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr') | |||||
| const cube = new Mesh( | |||||
| new BoxGeometry(1, 1, 1), | |||||
| new PhysicalMaterial({ | |||||
| // roughness: 0, | |||||
| // metalness: 1, | |||||
| })) | |||||
| const maps = [ | |||||
| 'https://threejs.org/examples/textures/sprite0.png', | |||||
| 'https://threejs.org/examples/textures/uv_grid_opengl.jpg', | |||||
| 'https://threejs.org/examples/models/svg/style-css-inside-defs.svg', | |||||
| 'https://cdn.jsdelivr.net/gh/Rabbid76/graphics-snippets/resource/texture/lookuptable.png', | |||||
| 'https://cdn.jsdelivr.net/gh/Rabbid76/graphics-snippets/resource/texture/perlin3_cp.png', | |||||
| 'https://cdn.jsdelivr.net/gh/Rabbid76/graphics-snippets/resource/texture/perlin4_cp.png', | |||||
| 'https://cdn.jsdelivr.net/gh/Rabbid76/graphics-snippets/resource/texture/ObjectSheet.png', | |||||
| 'https://cdn.jsdelivr.net/gh/Rabbid76/graphics-snippets/resource/texture/512x512_Texel_Density_Texture_1.png', | |||||
| 'https://cdn.jsdelivr.net/gh/Rabbid76/graphics-snippets/resource/texture/toy_box_normal.png', | |||||
| 'https://cdn.jsdelivr.net/gh/Rabbid76/graphics-snippets/resource/texture/example_1_texture.png', | |||||
| ] | |||||
| const bumps = [ | |||||
| maps[0], | |||||
| maps[1], | |||||
| maps[2], | |||||
| maps[3], | |||||
| maps[4], | |||||
| maps[5], | |||||
| maps[6], | |||||
| maps[7], | |||||
| 'https://cdn.jsdelivr.net/gh/Rabbid76/graphics-snippets/resource/texture/toy_box_disp.png', | |||||
| 'https://cdn.jsdelivr.net/gh/Rabbid76/graphics-snippets/resource/texture/example_1_heightmap.png', | |||||
| ] | |||||
| cube.material.bumpMap = await viewer.load<ITexture>(bumps[0]) || null | |||||
| cube.material.map = await viewer.load<ITexture>(maps[0]) || null | |||||
| cube.material.bumpScale = 0.1 | |||||
| viewer.scene.addObject(cube) | |||||
| ui.appendChild({ | |||||
| type: 'dropdown', | |||||
| value: maps[0], | |||||
| label: 'Bump Texture', | |||||
| children: ['none', ...maps].map((url: string) => ({ | |||||
| label: url.split('/').pop(), | |||||
| value: url, | |||||
| })), | |||||
| onChange: async(ev) => { | |||||
| console.log(ev.value) | |||||
| const url = ev.value | |||||
| const tex = await viewer.load<ITexture>(url) || null | |||||
| cube.material.map = tex | |||||
| const bumpUrl = bumps[maps.indexOf(url)] | |||||
| const bumpTex = await viewer.load<ITexture>(bumpUrl) || null | |||||
| cube.material.bumpMap = bumpTex | |||||
| cube.material.setDirty() | |||||
| }, | |||||
| }) | |||||
| ui.appendChild(cube.material.uiConfig) | |||||
| } | |||||
| init().finally(_testFinish) |
| export {ClearcoatTintPlugin} from './material/ClearcoatTintPlugin' | export {ClearcoatTintPlugin} from './material/ClearcoatTintPlugin' | ||||
| export {NoiseBumpMaterialPlugin} from './material/NoiseBumpMaterialPlugin' | export {NoiseBumpMaterialPlugin} from './material/NoiseBumpMaterialPlugin' | ||||
| export {CustomBumpMapPlugin} from './material/CustomBumpMapPlugin' | export {CustomBumpMapPlugin} from './material/CustomBumpMapPlugin' | ||||
| export {ParallaxMappingPlugin} from './material/ParallaxMappingPlugin' | |||||
| export {FragmentClippingExtensionPlugin, FragmentClippingMode} from './material/FragmentClippingExtensionPlugin' | export {FragmentClippingExtensionPlugin, FragmentClippingMode} from './material/FragmentClippingExtensionPlugin' | ||||
| // rendering | // rendering |
| import {MaterialExtension, updateMaterialDefines} from '../../materials' | |||||
| import {AViewerPluginSync, ThreeViewer} from '../../viewer' | |||||
| import {onChange, serialize} from 'ts-browser-helpers' | |||||
| import {uiFolderContainer, uiSlider, uiToggle} from 'uiconfig.js' | |||||
| import {shaderReplaceString} from '../../utils' | |||||
| import {ShaderChunk} from 'three' | |||||
| import {PhysicalMaterial} from '../../core' | |||||
| import ParallaxMappingPluginReliefShader from './shaders/ParallaxMappingPlugin.relief.glsl' | |||||
| /** | |||||
| * Parallax Mapping Plugin | |||||
| * Adds a material extension to PhysicalMaterial which parallax mapping to bump map in the material. | |||||
| * This is a port of Relief Parallax Mapping from [Rabbid76/graphics-snippets](https://github.com/Rabbid76/graphics-snippets/blob/master/html/technique/parallax_005_parallax_relief_mapping_derivative_tbn.html) | |||||
| * @category Plugins | |||||
| */ | |||||
| @uiFolderContainer('Parallax Mapping') | |||||
| export class ParallaxMappingPlugin extends AViewerPluginSync<''> { | |||||
| public static PluginType = 'ReliefParallaxMapping' | |||||
| @onChange(ParallaxMappingPlugin.prototype._updateExtension) | |||||
| @serialize() | |||||
| @uiToggle('Enabled') enabled = true | |||||
| @uiSlider('Step count', [1, 32], 1) | |||||
| @onChange(ParallaxMappingPlugin.prototype._updateExtension) | |||||
| @serialize() stepCount = 12 | |||||
| @uiSlider('Binary search steps', [1, 8], 1) | |||||
| @onChange(ParallaxMappingPlugin.prototype._updateExtension) | |||||
| @serialize() binaryStepCount = 3 | |||||
| @onChange(ParallaxMappingPlugin.prototype._updateExtension) | |||||
| @uiToggle('Debug Normals') debugNormals = false | |||||
| @onChange(ParallaxMappingPlugin.prototype._updateExtension) | |||||
| @uiToggle('Debug Hit Height') debugHitHeight = false | |||||
| private _defines: any = { | |||||
| ['PARALLAX_NORMAL_MAP_QUALITY']: 0, | |||||
| } | |||||
| constructor(enabled = true) { | |||||
| super() | |||||
| this.enabled = enabled | |||||
| this._updateExtension = this._updateExtension.bind(this) | |||||
| } | |||||
| private _updateExtension() { | |||||
| this._bumpMapExtension?.setDirty?.() | |||||
| this._viewer?.setDirty() | |||||
| } | |||||
| private _bumpMapExtension: MaterialExtension = { | |||||
| shaderExtender: (shader, material, _renderer) => { | |||||
| if (!material.bumpMap || this.isDisabled()) return | |||||
| shader.fragmentShader = shader.fragmentShader.replace('#include <normal_fragment_begin>', '') | |||||
| shader.fragmentShader = shader.fragmentShader.replace('#include <normal_fragment_maps>', '') | |||||
| shader.fragmentShader = shader.fragmentShader.replace('#include <map_fragment>', | |||||
| '#include <normal_fragment_begin>\n#include <normal_fragment_maps>\n#include <map_fragment>') | |||||
| for (const s of ['map_fragment', 'alphamap_fragment', 'roughnessmap_fragment', 'metalnessmap_fragment', 'emissivemap_fragment', 'transmission_fragment']) { | |||||
| shader.fragmentShader = shaderReplaceString(shader.fragmentShader, `#include <${s}>`, | |||||
| (ShaderChunk as any)[s].replace(/\bv\w+Uv\b/g, 'parallaxUv.xy', {replaceAll: true}) | |||||
| ) | |||||
| } | |||||
| if (this.debugNormals || this.debugHitHeight) | |||||
| shader.fragmentShader = shaderReplaceString(shader.fragmentShader, | |||||
| // .replace('texture2D( map, parallaxUv.xy )', 'texture2D( map, parallaxUv.xy )') | |||||
| 'texture2D( map, parallaxUv.xy )', | |||||
| this.debugNormals ? 'vec4(normal, 1.); normal = geometryNormal' : 'vec4(parallaxUv.z,0., 0., 1.)') | |||||
| shader.fragmentShader = shaderReplaceString(shader.fragmentShader, '#include <normal_fragment_maps>', | |||||
| shaderReplaceString( | |||||
| shaderReplaceString( | |||||
| ShaderChunk.normal_fragment_maps, | |||||
| '#elif defined( USE_NORMALMAP_TANGENTSPACE )', '#elif defined( USE_NORMALMAP_TANGENTSPACE ) && !defined( USE_BUMPMAP )'), | |||||
| 'normal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection );', | |||||
| // 'diffuseColor.rgb = vec3(0, dHdxy_fwd());' | |||||
| // 'diffuseColor.rgb = CalculateNormal(vUv).rgb;' | |||||
| 'vec3 parallaxUv = reliefParallaxPerturbNormal(faceDirection, normal);' | |||||
| ) | |||||
| ) | |||||
| }, | |||||
| parsFragmentSnippet: ()=> { | |||||
| return this.isDisabled() ? '' : (ParallaxMappingPluginReliefShader + '\n') | |||||
| .replaceAll('PARALLAX_MAP_STEPS', this.stepCount.toString()) // replacing here to unroll for loop. | |||||
| .replaceAll('PARALLAX_MAP_B_STEPS', this.binaryStepCount.toString()) | |||||
| }, | |||||
| isCompatible: (material: PhysicalMaterial) => { | |||||
| return material.isPhysicalMaterial | |||||
| }, | |||||
| computeCacheKey: material => { | |||||
| return '' + !this.isDisabled() + material.bumpMap?.uuid + this.debugNormals + this.debugHitHeight + this.stepCount.toString() + this.binaryStepCount.toString() | |||||
| }, | |||||
| onObjectRender: (_object, material, _renderer) => { | |||||
| if (this.isDisabled()) return // todo: use extraDefines | |||||
| updateMaterialDefines({ | |||||
| ...this._defines, | |||||
| }, material) | |||||
| }, | |||||
| } as MaterialExtension | |||||
| onAdded(viewer: ThreeViewer) { | |||||
| viewer.materialManager.registerMaterialExtension(this._bumpMapExtension) | |||||
| return super.onAdded(viewer) | |||||
| } | |||||
| onRemove(viewer: ThreeViewer) { | |||||
| viewer.materialManager.unregisterMaterialExtension(this._bumpMapExtension) | |||||
| return super.onRemove(viewer) | |||||
| } | |||||
| } |
| #ifdef USE_BUMPMAP | |||||
| mat3 mat3_inverse( mat3 A ) | |||||
| { | |||||
| mat3 M_t = mat3( | |||||
| vec3( A[0][0], A[1][0], A[2][0] ), | |||||
| vec3( A[0][1], A[1][1], A[2][1] ), | |||||
| vec3( A[0][2], A[1][2], A[2][2] ) ); | |||||
| float det = dot( cross( M_t[0], M_t[1] ), M_t[2] ); | |||||
| mat3 adjugate = mat3( cross( M_t[1], M_t[2] ), | |||||
| cross( M_t[2], M_t[0] ), | |||||
| cross( M_t[0], M_t[1] ) ); | |||||
| return adjugate / det; | |||||
| } | |||||
| float CalculateHeight( in vec2 texCoords ) | |||||
| { | |||||
| float height = texture2D( bumpMap, texCoords ).x; | |||||
| return clamp( height, 0.0, 1.0 ); | |||||
| } | |||||
| const vec2 bumpMapSize = vec2(512, 512); | |||||
| // Return normal in tangent space from normal map if available or bump map | |||||
| vec3 CalculateNormal( in vec2 texCoords ) | |||||
| { | |||||
| #if defined( TANGENTSPACE_NORMALMAP ) && 0 //todo: fix. not working properly. | |||||
| vec3 mapN = texture2D( normalMap, texCoords ).xyz; | |||||
| mapN.xy *= normalScale; | |||||
| return normalize( mapN ); | |||||
| #else | |||||
| vec2 texOffs = 1.0 / bumpMapSize; | |||||
| #if PARALLAX_NORMAL_MAP_QUALITY > 0 | |||||
| float hx[9]; | |||||
| hx[0] = texture2D( bumpMap, texCoords.st + texOffs * vec2(-1.0, -1.0) ).r; | |||||
| hx[1] = texture2D( bumpMap, texCoords.st + texOffs * vec2( 0.0, -1.0) ).r; | |||||
| hx[2] = texture2D( bumpMap, texCoords.st + texOffs * vec2( 1.0, -1.0) ).r; | |||||
| hx[3] = texture2D( bumpMap, texCoords.st + texOffs * vec2(-1.0, 0.0) ).r; | |||||
| hx[4] = texture2D( bumpMap, texCoords.st ).r; | |||||
| hx[5] = texture2D( bumpMap, texCoords.st + texOffs * vec2( 1.0, 0.0) ).r; | |||||
| hx[6] = texture2D( bumpMap, texCoords.st + texOffs * vec2(-1.0, 1.0) ).r; | |||||
| hx[7] = texture2D( bumpMap, texCoords.st + texOffs * vec2( 0.0, 1.0) ).r; | |||||
| hx[8] = texture2D( bumpMap, texCoords.st + texOffs * vec2( 1.0, 1.0) ).r; | |||||
| vec2 deltaH = vec2(hx[0]-hx[2] + 2.0*(hx[3]-hx[5]) + hx[6]-hx[8], hx[0]-hx[6] + 2.0*(hx[1]-hx[7]) + hx[2]-hx[8]); | |||||
| #else | |||||
| float h_xa = texture2D( bumpMap, texCoords.st + texOffs * vec2(-1.0, 0.0) ).r; | |||||
| float h_xb = texture2D( bumpMap, texCoords.st + texOffs * vec2( 1.0, 0.0) ).r; | |||||
| float h_ya = texture2D( bumpMap, texCoords.st + texOffs * vec2( 0.0, -1.0) ).r; | |||||
| float h_yb = texture2D( bumpMap, texCoords.st + texOffs * vec2( 0.0, 1.0) ).r; | |||||
| vec2 deltaH = vec2(h_xa-h_xb, h_ya-h_yb); | |||||
| #endif | |||||
| return normalize( vec3( deltaH / texOffs, 1.0 ) ); | |||||
| #endif | |||||
| } | |||||
| //https://github.com/Rabbid76/graphics-snippets/blob/master/html/technique/parallax_005_parallax_relief_mapping_derivative_tbn.html | |||||
| //https://web.archive.org/web/20190128023901/http://sunandblackcat.com/tipFullView.php?topicid=28 | |||||
| vec3 ReliefParallax( in float frontFace, in vec3 texDir3D, in vec2 texCoord ) | |||||
| { | |||||
| float surf_sign = frontFace; | |||||
| float back_face = step(0.0, -surf_sign); | |||||
| vec2 texStep = surf_sign * texDir3D.xy / abs(texDir3D.z); // (z is negative) the direction vector points downwards in tangent-space | |||||
| vec2 texC = texCoord.st + surf_sign * texStep + back_face * texStep.xy; | |||||
| float mapHeight = 1.0; | |||||
| float bumpHeightStep = 1.0 / float(PARALLAX_MAP_STEPS); | |||||
| float bestBumpHeight = mapHeight+bumpHeightStep; | |||||
| #pragma unroll_loop_start | |||||
| for ( int i = 0 ; i < PARALLAX_MAP_STEPS ; i ++ ) { | |||||
| if ( mapHeight < bestBumpHeight ) | |||||
| { | |||||
| bestBumpHeight -= bumpHeightStep; | |||||
| mapHeight = back_face + surf_sign * CalculateHeight(texC.xy - bestBumpHeight * texStep.xy); | |||||
| } | |||||
| } | |||||
| #pragma unroll_loop_end | |||||
| bestBumpHeight += bumpHeightStep; | |||||
| #pragma unroll_loop_start | |||||
| for ( int i = 0; i < PARALLAX_MAP_B_STEPS ; i ++ ) { | |||||
| bumpHeightStep *= 0.5; | |||||
| bestBumpHeight -= bumpHeightStep; | |||||
| mapHeight = back_face + surf_sign * CalculateHeight( texC.xy - bestBumpHeight * texStep.xy ); | |||||
| bestBumpHeight += ( bestBumpHeight < mapHeight ) ? bumpHeightStep : 0.0; | |||||
| } | |||||
| #pragma unroll_loop_end | |||||
| bestBumpHeight -= bumpHeightStep * clamp( ( bestBumpHeight - mapHeight ) / bumpHeightStep, 0.0, 1.0 ); | |||||
| mapHeight = bestBumpHeight; | |||||
| texC -= mapHeight * texStep; | |||||
| return vec3( texC.xy, mapHeight ); | |||||
| } | |||||
| vec3 reliefParallaxPerturbNormal(in float faceDirection, inout vec3 normal){ | |||||
| if(abs(bumpScale) < 0.001) return vec3(vBumpMapUv, 0.); | |||||
| // #ifdef DOUBLE_SIDED | |||||
| // | |||||
| // normal = normal * faceDirection; | |||||
| // | |||||
| // #endif | |||||
| float parallaxHeight; | |||||
| vec2 texCoords = vBumpMapUv; | |||||
| float face_sign = sign(dot(normal, vViewPosition)); | |||||
| // Followup: Normal Mapping Without Precomputed Tangents [http://www.thetenthplanet.de/archives/1180] | |||||
| vec3 N = normalize(normal); | |||||
| vec3 dp1 = dFdx(-vViewPosition); | |||||
| vec3 dp2 = dFdy(-vViewPosition); | |||||
| vec2 duv1 = dFdx(vBumpMapUv); | |||||
| vec2 duv2 = dFdy(vBumpMapUv); | |||||
| vec3 dp2perp = cross(dp2, N); | |||||
| vec3 dp1perp = cross(N, dp1); | |||||
| vec3 T = dp2perp * duv1.x + dp1perp * duv2.x; | |||||
| vec3 B = dp2perp * duv1.y + dp1perp * duv2.y; | |||||
| float invmax = inversesqrt(max(dot(T, T), dot(B, B))); | |||||
| mat3 tbnMat = mat3(T * invmax, B * invmax, N * bumpScale); | |||||
| vec3 tangentPos = normalize(mat3_inverse(tbnMat) * -vViewPosition); | |||||
| // vec2 parallaxUv = parallaxMapping(tangentPos, vBumpMapUv, parallaxHeight); | |||||
| vec3 parallaxUv = ReliefParallax(face_sign, tangentPos, vBumpMapUv); | |||||
| tbnMat[2] = face_sign * N / bumpScale; | |||||
| normal = normalize(tbnMat * CalculateNormal(parallaxUv.xy).xyz); | |||||
| //todo test this. | |||||
| #ifdef FLIP_SIDED | |||||
| normal = - normal; | |||||
| #endif | |||||
| // #ifdef DOUBLE_SIDED | |||||
| // | |||||
| // normal = normal * faceDirection; | |||||
| // | |||||
| // #endif | |||||
| // normal = geometryNormal; | |||||
| // todo: modify geometry.position (vViewPosition) for point, spot and area lights | |||||
| return parallaxUv; | |||||
| } | |||||
| #endif // USE_BUMPMAP |
| dependencies = [ProgressivePlugin] | dependencies = [ProgressivePlugin] | ||||
| constructor(rendersPerFrame = 1) { | |||||
| super() | |||||
| this.rendersPerFrame = rendersPerFrame | |||||
| } | |||||
| onAdded(viewer: ThreeViewer) { | onAdded(viewer: ThreeViewer) { | ||||
| super.onAdded(viewer) | super.onAdded(viewer) | ||||
| viewer.addEventListener('preRender', this._preRender) | viewer.addEventListener('preRender', this._preRender) |