<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/109/three.min.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/controls/OrbitControls.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/utils/BufferGeometryUtils.js"></script>


<script src="https://mevedia.com/share/ChainableOnBeforeCompile.js?8"></script>
html, body {
  height: 100%;
  margin: 0;
  overflow: hidden;
}
canvas {
  width: 100%;
  height: 100%;
  display; block;
}
var container, stats;

var camera, scene, renderer;

var mesh, light, ambient;

var floor;

var controls;

var texLoader = new THREE.TextureLoader();

THREE.MaterialCallback.use();

function init() {

  camera = new THREE.PerspectiveCamera( 25, window.innerWidth / window.innerHeight, 1, 10000 );
  camera.position.set( 100, 70, -360 );

  scene = new THREE.Scene();
  scene.fog = new THREE.FogExp2( 0xfff4e5, 0.0003 );

  ambient = new THREE.AmbientLight(0x7f7f7f);
  scene.add(ambient);

  light = new THREE.DirectionalLight( 0xffffff, 1.0 );
  light.position.set( 0, 1, 1 ).normalize();
  scene.add( light );

  var planeSimple = new THREE.PlaneGeometry( 500, 500 );
  var matSolid = new THREE.MeshStandardMaterial( { map: texLoader.load('https://www.the3rdsequence.com/texturedb/thumbnail/2/512/wild+grass.jpg',  t => {
    
    t.wrapS = t.wrapT = THREE.RepeatWrapping;
    t.repeat.set( 10, 10 );
    
  }) } );

  floor = new THREE.Mesh( planeSimple, matSolid );
  floor.rotation.x = - Math.PI / 2;
  scene.add( floor );

  renderer = new THREE.WebGLRenderer( { antialias: true } );
  renderer.setSize( window.innerWidth, window.innerHeight );
  renderer.setClearColor( scene.fog.color, 1 );
  renderer.debug.checkShaderErrors = true;
  
	//renderer.gammaInput = true;
	//renderer.gammaOutput = true;
	//renderer.gammaFactor = 2.2;  

  document.body.appendChild( renderer.domElement );

  controls = new THREE.OrbitControls(camera,renderer.domElement);
  
  loadGrass();
  
  loop();

}

function loop() {

  requestAnimationFrame( loop, renderer.domElement );

  if( controls ){

    controls.update();

  }

  renderer.render( scene, camera );

}

function loadGrass(){

	let cgeo, geos=[];

	const geo = new THREE.PlaneBufferGeometry(30, 30, 1, 1);
  
  geo.translate(0, 15, 0);
  
  for( let i = 0; i<500; i++ ){
  
  	cgeo = geo.clone();
    
  	cgeo.translate(rand(-210,210), 0, rand(-210,210));
  
  	cgeo.rotateY(Math.random()*Math.PI*2);    
    
    geos.push(cgeo);
  
  }
 
  const bgeo = THREE.BufferGeometryUtils.mergeBufferGeometries(geos);
  
	const tex = texLoader.load('https://www.titansoftime.com/img/grass/10.png');
  
	tex.wrapS = THREE.RepeatWrapping;
	tex.wrapT = THREE.ClampToEdgeWrapping;

	tex.repeat.set(1,1);  

	const grassMaterial = new THREE.MeshStandardMaterial({map:tex, alphaTest:0.5, side:THREE.DoubleSide});
  
  const NoisePlugin = {
  
    compile: function( shader ) {
      
      shader.vertexShader = shader.vertexShader.replace('#include <common>', `
#include <common>

#ifndef uTime
  uniform float uTime;
#endif

uniform float uSize;

float rand(float n){return fract(sin(n) * 43758.5453123);}

float noise(float p){
	float fl = floor(p);
  float fc = fract(p);
	return mix(rand(fl), rand(fl + 1.0), fc);
}
	
float noise(vec2 n) {
	const vec2 d = vec2(0.0, 1.0);
  vec2 b = floor(n), f = smoothstep(vec2(0.0), vec2(1.0), fract(n));
	return mix(mix(rand(b), rand(b + d.yx), f.x), mix(rand(b + d.xy), rand(b + d.yy), f.x), f.y);
}

`);
      
    }
    
  };
  
  const GrassPlugin = {
    time: 0,
    requires: [NoisePlugin],
    frame: function() {
      
      this.time += 0.025;
      
    },
    render: function( object, uniforms, context ) {
      
      context.set( uniforms.uTime, this.time );
      context.set( uniforms.uSize, object.waveLength );
      
    },
    compile: function( shader ) {
      
      shader.uniforms.uSize = {
        value: 0
      };
      shader.uniforms.uTime = {
        value: 0
      };
      
    shader.fragmentShader = shader.fragmentShader.replace(
      '#include <normal_fragment_begin>',
      'vec3 normal = normalize( vNormal );'
    );
      
      
      shader.vertexShader = shader.vertexShader.replace('#include <beginnormal_vertex>', `
#include <beginnormal_vertex>
objectNormal = vec3(0.0, 1.0, 0.0);

`);
      shader.vertexShader = shader.vertexShader.replace('#include <project_vertex>', `

vec4 GRASS_world = modelMatrix * vec4( transformed, 1.0 );
transformed.xz += uv.y * ( noise(GRASS_world.xz + uTime ) * uSize );

#include <project_vertex>
`);
      
    }
  };
  
  grassMaterial.onBeforeCompile = GrassPlugin;
  
  mesh = new THREE.Mesh(bgeo, grassMaterial);
  mesh.waveLength = 4.0;
  
  scene.add(mesh);
  

}



function rand(min, max) {
    return Math.random() * (max - min) + min;
}




init();
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.