【threejs开发随笔】利用shaderMaterial制作草地
【threejs開發隨筆】利用shaderMaterial制作草地
0.前言
最近的一個threejs項目需要草地的效果,遂參考各路大神的方法制作了一個
1.al-ro
2.spacejack
大體的思路是用shaderMaterial實現頂點動畫,模擬草葉隨風搖擺的樣子,利用InstancedMesh來大規模產生草葉。利用math庫里的MeshSurfaceSampler對象來吧草鋪到指定mesh上去。
點擊查看演示示例
點擊下載示例文件
1.先說用法
用法很簡單,引入makeGrass對象,然后new一個,傳入場景、需要生成草的mesh以及生成草的數量:
const grasses = new makeGrass(this.scene, // 場景model.scene.getObjectByName('平面') as Mesh, // 需要生草的mesh,類型需為Mesh250000 // 生草數量)然后在渲染循環中執行makeGrass實例的公共方法update()
...this.renderer.setAnimationLoop(() => { this.renderLoop() }) 執行渲染循環方法。...// 渲染循環private renderLoop () {this.renderer.render(this.scene, this.camera)this.grasses?.update()this.controls?.update()}2. 再說實現
首先我們新建一個makeGrass.ts文件,導入所需依賴
import { Clock, DoubleSide, InstancedMesh, Mesh, Object3D, PlaneGeometry, Scene, ShaderMaterial, Vector3 } from "three" import { MeshSurfaceSampler } from "three/examples/jsm/math/MeshSurfaceSampler"新建一個makeGrass類
export default class makeGrass {private scene:Sceneprivate mesh:Meshprivate leavesMaterial: ShaderMaterialprivate clock = new Clockprivate sampler!:MeshSurfaceSamplerprivate grassesAmount!:numberconstructor(scene:Scene,mesh:Mesh,grassesAmount:number,) {this.scene = scenethis.mesh = mesh} }寫一個方法用來初始化草葉的shader材質并返回
private initleavesMaterial() {const vertexShader = `varying vec2 vUv;uniform float time;// 噪波float N (vec2 st) {return fract( sin( dot( st.xy, vec2(12.9898,78.233 ) ) ) * 43758.5453123);}float smoothNoise( vec2 ip ){vec2 lv = fract( ip );vec2 id = floor( ip );lv = lv * lv * ( 3. - 2. * lv );float bl = N( id );float br = N( id + vec2( 1, 0 ));float b = mix( bl, br, lv.x );float tl = N( id + vec2( 0, 1 ));float tr = N( id + vec2( 1, 1 ));float t = mix( tl, tr, lv.x );return mix( b, t, lv.y );}void main() {vUv = uv;float t = time * 2.;// 頂點位置vec4 mvPosition = vec4( position, 1.0 );#ifdef USE_INSTANCINGmvPosition = instanceMatrix * mvPosition;#endif// 移動float noise = smoothNoise(mvPosition.xz * 0.5 + vec2(0., t));noise = pow(noise * 0.5 + 0.5, 2.) * 2.;// 葉片頂部晃動力度.float dispPower = 1. - cos( uv.y * 3.1416 * 0.5 );float displacement = noise * ( 0.3 * dispPower );mvPosition.z -= displacement;//vec4 modelViewPosition = modelViewMatrix * mvPosition;gl_Position = projectionMatrix * modelViewPosition;}`;const fragmentShader = `varying vec2 vUv;void main() {vec3 baseColor = vec3( 0.41, 1.0, 0.5 );float clarity = ( vUv.y * 0.5 ) + 0.5;gl_FragColor = vec4( baseColor * clarity, 1 );}`;const uniforms = {time: {value: 0}}const leavesMaterial = new ShaderMaterial({vertexShader,fragmentShader,uniforms,side: DoubleSide});return leavesMaterial}在構造函數中調用initleavesMaterial方法并賦值給類實例leavesMaterial
this.leavesMaterial = this.initleavesMaterial()初始化網格表面取樣器,用來在需要生成草的mesh上隨機取點,通過取到的點的位置來生成草
private initSampler(){this.sampler = new MeshSurfaceSampler(this.mesh).setWeightAttribute(null).build()}在構造函數中調用initSampler方法
this.initSampler()寫一個生成草的方法makegrasses
private makegrasses(grassesAmount:number){const instanceNumber = grassesAmount;const dummy = new Object3D();const geometry = new PlaneGeometry( 0.1, 1, 1, 1 );geometry.translate( 0, 0.5, 0 ); // 吧草葉的最低點設置到0.const instancedMesh = new InstancedMesh( geometry, this.leavesMaterial, instanceNumber );this.scene.add(instancedMesh)const _position = new Vector3();const_normal = new Vector3();for ( let i=0 ; i<instanceNumber ; i++ ) {this.sampler.sample(_position,_normal)_normal.add(_position)dummy.position.set(_position.x,_position.y,_position.z)dummy.scale.setScalar( 0.2 + Math.random() *0.6 );dummy.rotation.y = Math.random()* Math.PI;dummy.updateMatrix();instancedMesh.setMatrixAt( i, dummy.matrix );}}這里的草葉模型我們實例化一個簡單的PlaneGeometry,heightSegments參數控制草葉的高度分段,分段越多草葉在搖擺的時候的弧度越精細。而分段越多場景的總面數也就越多,出于性能考慮我分段只給了1,這樣草葉是筆直的在晃動。
最后我們寫一個公共方法update,用來刷新草葉動畫。
public update(){this.leavesMaterial.uniforms.time.value = this.clock.getElapsedTime();this.leavesMaterial.uniformsNeedUpdate = true;}總結
以上是生活随笔為你收集整理的【threejs开发随笔】利用shaderMaterial制作草地的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 不能为属性:[commandName]找
- 下一篇: RTX(2009)整合注意点