| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <title>three.js webgl - random - uv</title>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
- <link type="text/css" rel="stylesheet" href="main.css">
- </head>
- <body>
- <div id="container"></div>
- <div id="info">
- <a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - Random UV and Disolve
- </div>
- <script type="importmap">
- {
- "imports": {
- "three": "../build/three.module.js",
- "three/addons/": "./jsm/"
- }
- }
- </script>
- <script type="module">
- import * as THREE from 'three';
- import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
- import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
- import { HDRLoader } from 'three/addons/loaders/HDRLoader.js';
- import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
- import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
- let camera, scene, renderer, dirLight, ground, gui, material, materialIn, uniforms, uniformsIn;
- init();
- render();
- function init() {
- const container = document.getElementById( 'container' );
- camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 20 );
- camera.position.set( - 0.8, 0.6, 1.5 );
- scene = new THREE.Scene();
- renderer = new THREE.WebGLRenderer( { antialias: true } );
- renderer.setPixelRatio( window.devicePixelRatio );
- renderer.setSize( window.innerWidth, window.innerHeight );
- renderer.toneMapping = THREE.ACESFilmicToneMapping;
- renderer.toneMappingExposure = 0.7;
- renderer.shadowMap.enabled = true;
- renderer.shadowMap.type = THREE.VSMShadowMap;
- container.appendChild( renderer.domElement );
- dirLight = new THREE.DirectionalLight( 0xFFFFFF, 3 );
- dirLight.position.set( - 0.5, 1, 0.8 );
- dirLight.castShadow = true;
- scene.add( dirLight );
- const shadow = dirLight.shadow;
- shadow.mapSize.width = shadow.mapSize.height = 1024;
- shadow.radius = 16;
- shadow.bias = - 0.0005;
- const shadowCam = shadow.camera, s = 2;
- shadowCam.near = 0.5;
- shadowCam.far = 3;
- shadowCam.right = shadowCam.top = s;
- shadowCam.left = shadowCam.bottom = - s;
- // debug shadow
- //scene.add( new THREE.CameraHelper(shadowCam) );
- // add ground plane
- const plane = new THREE.PlaneGeometry( 2, 2 );
- plane.rotateX( - Math.PI * 0.5 );
- ground = new THREE.Mesh( plane, new THREE.ShadowMaterial( { opacity: 0.5 } ) );
- ground.receiveShadow = true;
- ground.position.z = - 0.5;
- scene.add( ground );
- const map = new THREE.TextureLoader().load( 'textures/jade.jpg' );
- map.colorSpace = THREE.SRGBColorSpace;
- map.wrapS = map.wrapT = THREE.RepeatWrapping;
- map.repeat.set( 20, 20 );
- map.flipY = false;
- const disolveMap = new THREE.TextureLoader().load( 'textures/shaderball_ds.jpg' );
- disolveMap.flipY = false;
- const noise = new THREE.TextureLoader().load( 'textures/noise.png' );
- new HDRLoader()
- .setPath( 'textures/equirectangular/' )
- .load( 'lobe.hdr', function ( texture ) {
- texture.mapping = THREE.EquirectangularReflectionMapping;
- scene.background = texture;
- scene.environment = texture;
- scene.backgroundBlurriness = 0.5;
- scene.backgroundIntensity = 1.0;
- scene.environmentIntensity = 1.5;
- render();
- // model
- const loader = new GLTFLoader().setPath( 'models/gltf/' );
- loader.setDRACOLoader( new DRACOLoader().setDecoderPath( 'jsm/libs/draco/gltf/' ) );
- loader.load( 'ShaderBall2.glb', function ( gltf ) {
- const shaderBall = gltf.scene.children[ 0 ];
- // shaderBall is a group with 3 children : base, inside and logo
- // ao map is include in model
- let i = shaderBall.children.length, n = 0;
- while ( i -- ) {
- shaderBall.children[ i ].receiveShadow = true;
- shaderBall.children[ i ].castShadow = true;
- shaderBall.children[ i ].renderOrder = n ++;
- }
- material = shaderBall.children[ 0 ].material;
- material.map = map;
- material.alphaMap = disolveMap;
- material.transparent = true;
- materialIn = shaderBall.children[ 1 ].material;
- materialIn.alphaMap = disolveMap;
- materialIn.transparent = true;
- material.onBeforeCompile = function ( shader ) {
- shader.uniforms[ 'disolve' ] = { value: 0 };
- shader.uniforms[ 'threshold' ] = { value: 0.2 };
- shader.uniforms[ 'noiseMap' ] = { value: noise };
- shader.uniforms[ 'enableRandom' ] = { value: 1 };
- shader.uniforms[ 'useNoiseMap' ] = { value: 1 };
- shader.uniforms[ 'useSuslikMethod' ] = { value: 0 };
- shader.uniforms[ 'debugNoise' ] = { value: 0 };
- shader.fragmentShader = shader.fragmentShader.replace( '#include <clipping_planes_pars_fragment>', '#include <clipping_planes_pars_fragment>' + randomUV );
- shader.fragmentShader = shader.fragmentShader.replace( '#include <map_fragment>', map_fragment );
- // for disolve
- shader.fragmentShader = shader.fragmentShader.replace( '#include <alphamap_pars_fragment>', alphamap_pars_fragment );
- shader.fragmentShader = shader.fragmentShader.replace( '#include <alphamap_fragment>', alphamap_fragment );
- uniforms = shader.uniforms;
- };
- materialIn.onBeforeCompile = function ( shader ) {
- shader.uniforms[ 'disolve' ] = { value: 0 };
- shader.uniforms[ 'threshold' ] = { value: 0.2 };
- // for disolve
- shader.fragmentShader = shader.fragmentShader.replace( '#include <alphamap_pars_fragment>', alphamap_pars_fragment );
- shader.fragmentShader = shader.fragmentShader.replace( '#include <alphamap_fragment>', alphamap_fragment );
- uniformsIn = shader.uniforms;
- };
- scene.add( shaderBall );
- render();
- createGUI();
- } );
- } );
- const controls = new OrbitControls( camera, renderer.domElement );
- controls.addEventListener( 'change', render ); // use if there is no animation loop
- controls.minDistance = 0.3;
- controls.maxDistance = 10;
- controls.target.set( 0, 0.4, 0 );
- controls.update();
- window.addEventListener( 'resize', onWindowResize );
- }
- function createGUI() {
- const setting = {
- get 'Enabled'() {
- return uniforms.enableRandom.value ? true : false;
- },
- set 'Enabled'( v ) {
- uniforms.enableRandom.value = v ? 1 : 0; render();
- },
- get 'UseNoiseMap'() {
- return uniforms.useNoiseMap.value ? true : false;
- },
- set 'UseNoiseMap'( v ) {
- uniforms.useNoiseMap.value = v ? 1 : 0; render();
- },
- get 'SuslikMethod'() {
- return uniforms.useSuslikMethod.value ? true : false;
- },
- set 'SuslikMethod'( v ) {
- uniforms.useSuslikMethod.value = v ? 1 : 0; render();
- },
- get 'DebugNoise'() {
- return uniforms.debugNoise.value ? true : false;
- },
- set 'DebugNoise'( v ) {
- uniforms.debugNoise.value = v ? 1 : 0; render();
- },
- // disolve
- get 'disolve'() {
- return uniforms.disolve.value;
- },
- set 'disolve'( v ) {
- uniforms.disolve.value = v; uniformsIn.disolve.value = v; ground.material.opacity = ( 1 - v ) * 0.5; render();
- },
- get 'threshold'() {
- return uniforms.threshold.value;
- },
- set 'threshold'( v ) {
- uniforms.threshold.value = v; uniformsIn.threshold.value = v; render();
- }
- };
- gui = new GUI();
- gui.add( material, 'roughness', 0, 1, 0.01 ).onChange( render );
- gui.add( material, 'metalness', 0, 1, 0.01 ).onChange( render );
- gui.add( setting, 'disolve', 0, 1, 0.01 ).onChange( render );
- gui.add( setting, 'threshold', 0, 1, 0.01 ).onChange( render );
- gui.add( setting, 'Enabled' );
- gui.add( setting, 'UseNoiseMap' );
- gui.add( setting, 'SuslikMethod' );
- gui.add( setting, 'DebugNoise' );
- }
- function onWindowResize() {
- camera.aspect = window.innerWidth / window.innerHeight;
- camera.updateProjectionMatrix();
- renderer.setSize( window.innerWidth, window.innerHeight );
- render();
- }
- //
- function render() {
- renderer.render( scene, camera );
- }
- const randomUV = /* glsl */ `
- uniform sampler2D noiseMap;
- uniform float enableRandom;
- uniform float useNoiseMap;
- uniform float debugNoise;
- uniform float useSuslikMethod;
- float directNoise(vec2 p){
- vec2 ip = floor(p);
- vec2 u = fract(p);
- u = u*u*(3.0-2.0*u);
- float res = mix(
- mix(rand(ip),rand(ip+vec2(1.0,0.0)),u.x),
- mix(rand(ip+vec2(0.0,1.0)),rand(ip+vec2(1.0,1.0)),u.x),u.y);
- return res*res;
- }
- float sum( vec4 v ) { return v.x+v.y+v.z; }
- vec4 textureNoTile( sampler2D mapper, in vec2 uv ){
- // sample variation pattern
- float k = 0.0;
- if( useNoiseMap == 1.0 ) k = texture2D( noiseMap, 0.005*uv ).x;
- else k = directNoise( uv );
- // compute index
- float index = k*8.0;
- float f = fract( index );
- float ia = 0.0;
- float ib = 0.0;
- if( useSuslikMethod == 1.0 ){
- ia = floor(index+0.5);
- ib = floor(index);
- f = min(f, 1.0-f)*2.0;
- } else {
- ia = floor( index );
- ib = ia + 1.0;
- }
- // offsets for the different virtual patterns
- vec2 offa = sin(vec2(3.0,7.0)*ia); // can replace with any other hash
- vec2 offb = sin(vec2(3.0,7.0)*ib); // can replace with any other hash
- // compute derivatives for mip-mapping
- vec2 dx = dFdx(uv);
- vec2 dy = dFdy(uv);
- // sample the two closest virtual patterns
- vec4 cola = textureGrad( mapper, uv + offa, dx, dy );
- vec4 colb = textureGrad( mapper, uv + offb, dx, dy );
- if( debugNoise == 1.0 ){
- cola = vec4( 0.1,0.0,0.0,1.0 );
- colb = vec4( 0.0,0.0,1.0,1.0 );
- }
- // interpolate between the two virtual patterns
- return mix( cola, colb, smoothstep(0.2,0.8,f-0.1*sum(cola-colb)) );
- }`;
- const map_fragment = /* glsl */ `
- #ifdef USE_MAP
- if( enableRandom == 1.0 ) diffuseColor *= textureNoTile( map, vMapUv );
- else diffuseColor *= texture2D( map, vMapUv );
- #endif
- `;
- const alphamap_pars_fragment = /* glsl */ `
- #ifdef USE_ALPHAMAP
- uniform sampler2D alphaMap;
- uniform float disolve;
- uniform float threshold;
- #endif
- `;
- const alphamap_fragment = /* glsl */ `
- #ifdef USE_ALPHAMAP
- float vv = texture2D( alphaMap, vAlphaMapUv ).g;
- float r = disolve * (1.0 + threshold * 2.0) - threshold;
- float mixf = clamp((vv - r)*(1.0/threshold), 0.0, 1.0);
- diffuseColor.a = mixf;
- #endif
- `;
- </script>
- </body>
- </html>
|