Three.js 冬季树木

文化   2024-09-13 18:00   河南  

使用 Three.js 的 WebGL 小实验。冬季树木。

实现代码

HTML:

<img src="https://cors-anywhere.herokuapp.com/http://www.radiolights.com/CODEPEN/b-grasslight-big2.jpg" class="hidden" /><img src="https://cors-anywhere.herokuapp.com/http://www.radiolights.com/CODEPEN/b-backgrounddetailed62.jpg" class="hidden" /><img src="https://cors-anywhere.herokuapp.com/http://www.radiolights.com/CODEPEN/b-grasslight-big-nm2.jpg" class="hidden" />

<script id="fragmentShaderNoise" type="x-shader/x-fragment">

//// Description : Array and textureless GLSL 3D simplex noise function.// Author : Ian McEwan, Ashima Arts.// Maintainer : ijm// Lastmod : 20110409 (stegu)// License : Copyright (C) 2011 Ashima Arts. All rights reserved.// Distributed under the MIT License. See LICENSE file.//

uniform float time;varying vec2 vUv;

vec4 permute( vec4 x ) {

return mod( ( ( x * 34.0 ) + 1.0 ) * x, 289.0 );

}

vec4 taylorInvSqrt( vec4 r ) {

return 1.79284291400159 - 0.85373472095314 * r;

}

float snoise( vec3 v ) {

const vec2 C = vec2( 1.0 / 6.0, 1.0 / 3.0 );const vec4 D = vec4( 0.0, 0.5, 1.0, 2.0 );

// First corner

vec3 i = floor( v + dot( v, C.yyy ) );vec3 x0 = v - i + dot( i, C.xxx );

// Other corners

vec3 g = step( x0.yzx, x0.xyz );vec3 l = 1.0 - g;vec3 i1 = min( g.xyz, l.zxy );vec3 i2 = max( g.xyz, l.zxy );

vec3 x1 = x0 - i1 + 1.0 * C.xxx;vec3 x2 = x0 - i2 + 2.0 * C.xxx;vec3 x3 = x0 - 1. + 3.0 * C.xxx;

// Permutations

i = mod( i, 289.0 );vec4 p = permute( permute( permute( i.z + vec4( 0.0, i1.z, i2.z, 1.0 ) ) + i.y + vec4( 0.0, i1.y, i2.y, 1.0 ) ) + i.x + vec4( 0.0, i1.x, i2.x, 1.0 ) );

// Gradients// ( N*N points uniformly over a square, mapped onto an octahedron.)

float n_ = 1.0 / 7.0; // N=7

vec3 ns = n_ * D.wyz - D.xzx;

vec4 j = p - 49.0 * floor( p * ns.z *ns.z ); // mod(p,N*N)

vec4 x_ = floor( j * ns.z );vec4 y_ = floor( j - 7.0 * x_ ); // mod(j,N)

vec4 x = x_ *ns.x + ns.yyyy;vec4 y = y_ *ns.x + ns.yyyy;vec4 h = 1.0 - abs( x ) - abs( y );

vec4 b0 = vec4( x.xy, y.xy );vec4 b1 = vec4( x.zw, y.zw );



vec4 s0 = floor( b0 ) * 2.0 + 1.0;vec4 s1 = floor( b1 ) * 2.0 + 1.0;vec4 sh = -step( h, vec4( 0.0 ) );

vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww;

vec3 p0 = vec3( a0.xy, h.x );vec3 p1 = vec3( a0.zw, h.y );vec3 p2 = vec3( a1.xy, h.z );vec3 p3 = vec3( a1.zw, h.w );

// Normalise gradients

vec4 norm = taylorInvSqrt( vec4( dot( p0, p0 ), dot( p1, p1 ), dot( p2, p2 ), dot( p3, p3 ) ) );p0 *= norm.x;p1 *= norm.y;p2 *= norm.z;p3 *= norm.w;

// Mix final noise value

vec4 m = max( 0.6 - vec4( dot( x0, x0 ), dot( x1, x1 ), dot( x2, x2 ), dot( x3, x3 ) ), 0.0 );m = m * m;return 42.0 * dot( m*m, vec4( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 ), dot( p3, x3 ) ) );

}

float surface3( vec3 coord ) {

float n = 0.0;

n += 1.0 * abs( snoise( coord ) );n += 0.5 * abs( snoise( coord * 2.0 ) );n += 0.25 * abs( snoise( coord * 4.0 ) );n += 0.125 * abs( snoise( coord * 8.0 ) );

return n;

}

void main( void ) {

vec3 coord = vec3( vUv, -time );float n = surface3( coord );

gl_FragColor = vec4( vec3( n, n, n ), 1.0 );

}

</script>

<script id="vertexShader" type="x-shader/x-vertex">

varying vec2 vUv;uniform vec2 scale;uniform vec2 offset;

void main( void ) {

vUv = uv * scale + offset;gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );

}

</script>

<div class="link"> <a href="https://twitter.com/cjgammon">@cjgammon</a></div>

<div id="loader"> <h1>wait for it...</h1></div><div id="container"></div>
CSS:
html{  min-height: 100%;}

canvas{ background: transparent; opacity: 0;}

body { margin: 0; overflow: hidden; min-height: 100%; background: #050505;background-repeat: no-repeat; background-position: center;}

#loader{ position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; h1{ color: white; letter-spacing: .1em; font-weight: 100; font-family: sans-serif; }}

.link{ position: fixed; bottom: 0; left: 0; margin: 10px; font-family: sans-serif; font-weight: 100; a{ color: black; text-decoration: none; &:hover{ text-decoration: underline; } }}

.hidden{ display: none;}

JAVASCRIPT:

if (!Detector.webgl) Detector.addGetWebGLMessage();

var SCREEN_WIDTH = window.innerWidth;var SCREEN_HEIGHT = window.innerHeight;

var renderer, container, stats;

var camera, scene;var cameraOrtho, sceneRenderTarget;

var uniformsNoise, uniformsNormal, heightMap, normalMap, quadTarget;

var directionalLight, pointLight;

var terrain;

var specularMap;

var textureCounter = 0;

var animDelta = 0, animDeltaDir = -1;var lightVal = 0, lightDir = 1;

var clock = new THREE.Clock();

var morph, morphs = [];

var updateNoise = true;

var animateTerrain = false;

var textMesh1;var diffuseTexture1, diffuseTexture2, detailTexture;

var pars = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat};

container = document.getElementById("container");

// SCENE (FINAL)

scene = new THREE.Scene();scene.fog = new THREE.Fog(0x050505, 2000, 4000);

// RENDERER

renderer = new THREE.WebGLRenderer();renderer.setClearColor(scene.fog.color);renderer.setPixelRatio(window.devicePixelRatio);renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);renderer.shadowMap.enabled = true;renderer.shadowMap.type = THREE.PCFSoftShadowMap;container.appendChild(renderer.domElement);

//

renderer.gammaInput = true;renderer.gammaOutput = true;

// SCENE (RENDER TARGET)

sceneRenderTarget = new THREE.Scene();

cameraOrtho = new THREE.OrthographicCamera( SCREEN_WIDTH / -2, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, SCREEN_HEIGHT / -2, -10000, 10000);cameraOrtho.position.z = 100;

sceneRenderTarget.add(cameraOrtho);

specularMap = new THREE.WebGLRenderTarget(2048, 2048, pars);specularMap.texture.generateMipmaps = false;

var mlib = {};var loader = new THREE.TextureLoader();loader.crossOrigin = "";loader.load( "https://cors-anywhere.herokuapp.com/http://www.radiolights.com/CODEPEN/b-grasslight-big2.jpg", function (texture) { diffuseTexture1 = texture; loadTextures(); applyShader(THREE.LuminosityShader, diffuseTexture1, specularMap); });

loader.load( "https://cors-anywhere.herokuapp.com/http://www.radiolights.com/CODEPEN/b-backgrounddetailed62.jpg", function (texture) { diffuseTexture2 = texture; loadTextures(); });

loader.load( "https://cors-anywhere.herokuapp.com/http://www.radiolights.com/CODEPEN/b-grasslight-big-nm2.jpg", function (texture) { detailTexture = texture; loadTextures(); });

function init() { // CAMERA

camera = new THREE.PerspectiveCamera( 40, SCREEN_WIDTH / SCREEN_HEIGHT, 2, 4000 ); camera.position.set(-1200, 800, 1200);

console.log(camera);

controls = new THREE.OrbitControls(camera); controls.target.set(0, 0, 0);

controls.rotateSpeed = 1.0; controls.zoomSpeed = 1.2; controls.panSpeed = 0.8; controls.maxPolarAngle = (90 * Math.PI) / 180;

controls.keys = [65, 83, 68];

// LIGHTS

scene.add(new THREE.AmbientLight(0x556380));

directionalLight = new THREE.DirectionalLight(0x8d4a23, 0.12); directionalLight.position.set(1500, 3000, 0); directionalLight.castShadow = true; directionalLight.shadowCameraNear = 50; directionalLight.shadowCameraFar = 10000; directionalLight.shadowCameraLeft = -2000; directionalLight.shadowCameraRight = 2000; directionalLight.shadowCameraTop = 2000; directionalLight.shadowCameraBottom = -2000; directionalLight.shadowMapWidth = 1024; directionalLight.shadowMapHeight = 1024; //directionalLight.shadowCameraVisible = true;

scene.add(directionalLight);

pointLight = new THREE.PointLight(0x594b1d, 4, 4000); pointLight.position.set(1000, 0, 0); scene.add(pointLight);

// HEIGHT + NORMAL MAPS

var normalShader = THREE.NormalMapShader;

var rx = 256, ry = 256;

heightMap = new THREE.WebGLRenderTarget(rx, ry, pars); heightMap.texture.generateMipmaps = false;

normalMap = new THREE.WebGLRenderTarget(rx, ry, pars); normalMap.texture.generateMipmaps = false;

uniformsNoise = { time: { type: "f", value: 1.0 }, scale: { type: "v2", value: new THREE.Vector2(1.5, 1.5) }, offset: { type: "v2", value: new THREE.Vector2(0, 0) } };

uniformsNormal = THREE.UniformsUtils.clone(normalShader.uniforms);

uniformsNormal.height.value = 0.05; uniformsNormal.resolution.value.set(rx, ry); uniformsNormal.heightMap.value = heightMap;

var vertexShader = document.getElementById("vertexShader").textContent;

// TEXTURES diffuseTexture1.wrapS = diffuseTexture1.wrapT = THREE.RepeatWrapping; diffuseTexture2.wrapS = diffuseTexture2.wrapT = THREE.RepeatWrapping; detailTexture.wrapS = detailTexture.wrapT = THREE.RepeatWrapping; specularMap.wrapS = specularMap.wrapT = THREE.RepeatWrapping;

// TERRAIN SHADER

var params = [ [ "heightmap", document.getElementById("fragmentShaderNoise").textContent, vertexShader, uniformsNoise, false], [ "normal", normalShader.fragmentShader, normalShader.vertexShader, uniformsNormal, false] ];

for (var i = 0; i < params.length; i++) { material = new THREE.ShaderMaterial({ uniforms: params[i][3], vertexShader: params[i][2], fragmentShader: params[i][1], lights: params[i][4], fog: true });

mlib[params[i][0]] = material; }

var plane = new THREE.PlaneBufferGeometry(SCREEN_WIDTH, SCREEN_HEIGHT);

quadTarget = new THREE.Mesh( plane, new THREE.MeshBasicMaterial({ color: 0x000000 }) ); quadTarget.position.z = -500; sceneRenderTarget.add(quadTarget);

// TERRAIN MESH

var geometryTerrain = new THREE.PlaneBufferGeometry(6000, 6000, 256, 256);

THREE.BufferGeometryUtils.computeTangents(geometryTerrain);

terrain = new THREE.Mesh( geometryTerrain, new THREE.MeshLambertMaterial({ color: 0xe2c5be, map: diffuseTexture1, aoMap: detailTexture, lightMap: diffuseTexture1 }) );

terrain.position.set(0, -125, 0); terrain.rotation.x = -Math.PI / 2; terrain.visible = true; terrain.receiveShadow = true; scene.add(terrain);

// EVENTS

onWindowResize();

window.addEventListener("resize", onWindowResize, false);

// COMPOSER

renderer.autoClear = false;

renderTargetParameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat, stencilBuffer: false };

renderTarget = new THREE.WebGLRenderTarget( SCREEN_WIDTH, SCREEN_HEIGHT, renderTargetParameters ); renderTarget.texture.generateMipmaps = false;

effectBloom = new THREE.BloomPass(0.6); var effectBleach = new THREE.ShaderPass(THREE.BleachBypassShader);

hblur = new THREE.ShaderPass(THREE.HorizontalTiltShiftShader); vblur = new THREE.ShaderPass(THREE.VerticalTiltShiftShader);

var bluriness = 6;

hblur.uniforms["h"].value = bluriness / SCREEN_WIDTH; vblur.uniforms["v"].value = bluriness / SCREEN_HEIGHT;

hblur.uniforms["r"].value = vblur.uniforms["r"].value = 0.5;

effectBleach.uniforms["opacity"].value = 0.65;

composer = new THREE.EffectComposer(renderer, renderTarget);

var renderModel = new THREE.RenderPass(scene, camera);

vblur.renderToScreen = true;

composer = new THREE.EffectComposer(renderer, renderTarget);

composer.addPass(renderModel);

composer.addPass(effectBloom); //composer.addPass( effectBleach );

composer.addPass(hblur); composer.addPass(vblur);

var size = 3000; for (i = 0; i < 200; i += 1) { addTree(-size / 2 + Math.random() * size, -size / 2 + Math.random() * size); }

var loader = document.getElementById("loader"); loader.style.display = "none";

var canvas = document.getElementsByTagName("canvas"); canvas[0].style.opacity = "1";}

function addTree(x, z) { var group = new THREE.Object3D();

// radiusAtTop, radiusAtBottom, height, segmentsAroundRadius, segmentsAlongHeight, var geo = new THREE.CylinderGeometry(15, 20, 150, 6, 4); var mat = new THREE.MeshPhongMaterial({ color: 0xe66f73, shading: THREE.FlatShading });

var shape = new THREE.Mesh(geo, mat); shape.position.set(0, -50, 0); shape.castShadow = true; shape.receiveShadow = true; group.add(shape);

var size = Math.round(Math.random()); var s = 0.3 + Math.random() * 0.7;

//top colors var r2 = 246; var g2 = 205; var b2 = 118;

//bottom colors var r1 = 253; var g1 = 240; var b1 = 205;

var l = 5 + size;

for (i = 0; i < l; i += 1) { var r = r1 + (i / l) * (r2 - r1); var g = g1 + (i / l) * (g2 - g1); var b = b1 + (i / l) * (b2 - b1);

c = new THREE.Color(r / 255, g / 255, b / 255); //0xf6ce76;

// pyramid var geo = new THREE.CylinderGeometry(i * 5, 50 + i * 10, 75, 5, 5); var mat = new THREE.MeshPhongMaterial({ color: c, shading: THREE.FlatShading });

if (i > 0) { rot = -0.05 + Math.random() * 0.1; } else { rot = 0; }

var shape = new THREE.Mesh(geo, mat); shape.position.set(0, 150 - i * 35, 0); shape.rotation.set(rot, 0, rot); shape.castShadow = true; shape.receiveShadow = true; group.add(shape); }

var y = 0 - (1 - s) * 150; group.position.set(x, y, z); group.scale.set(s, s, s); scene.add(group);}

function onWindowResize(event) { SCREEN_WIDTH = window.innerWidth; SCREEN_HEIGHT = window.innerHeight;

renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);

camera.aspect = SCREEN_WIDTH / SCREEN_HEIGHT; camera.updateProjectionMatrix();}

function applyShader(shader, texture, target) { var shaderMaterial = new THREE.ShaderMaterial({ fragmentShader: shader.fragmentShader, vertexShader: shader.vertexShader, uniforms: THREE.UniformsUtils.clone(shader.uniforms) });

shaderMaterial.uniforms["tDiffuse"].value = texture;

var sceneTmp = new THREE.Scene();

var meshTmp = new THREE.Mesh( new THREE.PlaneBufferGeometry(SCREEN_WIDTH, SCREEN_HEIGHT), shaderMaterial ); meshTmp.position.z = -500;

sceneTmp.add(meshTmp);

renderer.render(sceneTmp, cameraOrtho, target, true);}

//

function loadTextures() { textureCounter += 1;

if (textureCounter == 3) { init(); animate(); }}

//

function animate() { requestAnimationFrame(animate);

render();}

function render() { var delta = clock.getDelta();

if (terrain) { controls.update();

var time = Date.now() * 0.001;

var fLow = 0.1, fHigh = 0.6;

lightVal = THREE.Math.clamp(lightVal + 0.5 * delta * lightDir, fLow, fHigh);

var valNorm = (lightVal - fLow) / (fHigh - fLow);

scene.fog.color.setHSL(2.6, 0.1, lightVal);

renderer.setClearColor(scene.fog.color);

directionalLight.intensity = THREE.Math.mapLinear(valNorm, 0, 1, 0.1, 1.15); pointLight.intensity = THREE.Math.mapLinear(valNorm, 0, 1, 0.9, 1.5);

if (updateNoise) { animDelta = THREE.Math.clamp(animDelta + 0.00075 * animDeltaDir, 0, 0.05);

quadTarget.material = mlib["heightmap"]; renderer.render(sceneRenderTarget, cameraOrtho, heightMap, true);

quadTarget.material = mlib["normal"]; renderer.render(sceneRenderTarget, cameraOrtho, normalMap, true); }

composer.render(0.1); }}



源码:

https://codepen.io/cjgammon/pen/ONWeZX


体验:

https://codepen.io/cjgammon/full/ONWeZX



感谢您的阅读      

在看点赞 好文不断  

初识Threejs
初识 Three.js 的奇妙世界,走进三维空间,与小编一起拥抱前端技术,涉及WebGL、WebGPU、Threejs、Shader、GIS、VR/AR、数字孪生、3D图形学等。
 最新文章