Anson Chan
Anson Chan

Reputation: 25

How to get correct shadow on ShaderMaterial

I have used InstancedBufferGeometry to create a lot of box then update position with Perlin noise. But the mesh has cast and has received wrong shadow. How to calculate a right shadow?

vertexShader

attribute vec3 offset;
attribute vec4 orientation;
attribute vec3 color;

varying vec3 pos;
varying vec3 vNormal;
varying vec3 vWorldPosition;
varying vec3 vColor;

vec3 applyQuaternionToVector( vec4 q, vec3 v ){
    return v + 2.0 * cross( q.xyz, cross( q.xyz, v ) + q.w * v );
}

THREE.ShaderChunk["common"]
THREE.ShaderChunk["shadowmap_pars_vertex"]

void main() {
    vColor = color;

    vec3 vPosition = applyQuaternionToVector( orientation, position );
    pos = vPosition + offset;

    vNormal = normalMatrix * vec3(normal + normalize(offset) * 0.3);

    vec4 worldPosition = modelMatrix * vec4(pos, 1.0);
    vWorldPosition = worldPosition.xyz;

    gl_Position = projectionMatrix * modelViewMatrix * worldPosition;
    THREE.ShaderChunk["shadowmap_vertex"]
}

fragmentShader

THREE.ShaderChunk['common']
THREE.ShaderChunk['packing']
varying vec3 pos;
varying vec3 vNormal;
varying vec3 vWorldPosition;
varying vec3 vColor;

uniform vec3 lightPosition;

THREE.ShaderChunk['shadowmap_pars_fragment']
void main() {
    vec3 lightDirection = normalize(lightPosition + pos);

    float c = max(0.0, dot(vNormal, lightDirection)) * 2.;
    gl_FragColor = vec4(.3+c , .3+c , .3+c , 1.);
    THREE.ShaderChunk['shadowmap_fragment']
}

demo link is here

three.js r.106
Thanks

var scene, camera, renderer;
var plane, temp, vnh, point;
var radius = 10;
var stats = new Stats();
var start = Date.now();
var options = {
  scale: 200,
  density: 2.5
}
var currentQ = new THREE.Quaternion();
var initedBoxes = false;

var init = function() {
  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
  camera.position.z = 25;
  var controls = new THREE.OrbitControls(camera);

  renderer = new THREE.WebGLRenderer();
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  document.body.appendChild(renderer.domElement);

  var gui = new dat.GUI({
    width: 300
  });
  gui.add(options, 'scale').min(0).max(200).name('Scale');
  gui.add(options, 'density').min(0).max(10).name('Density');

  // stats
  stats.showPanel(0);
  stats.domElement.style.position = 'fixed';
  stats.domElement.style.top = 0;
  document.body.appendChild(stats.domElement);

  initLights();
  initSphere();
  initBoxes();

  renderer.setAnimationLoop(function() {
    update();
    render();
  });
}

var initLights = function() {
  var ambientLight = new THREE.AmbientLight(0x555555);
  scene.add(ambientLight);
  light1 = new THREE.SpotLight(0xffffff, 2, 200, 10);
  light1.position.set(-30, 30, 40);
  light1.castShadow = true;
  scene.add(light1);

  light1Helper = new THREE.SpotLightHelper(light1, 0xffffff);
  scene.add(light1Helper);
}

var initSphere = function() {
  var geometry = new THREE.IcosahedronGeometry(radius, 3);
  var material = new THREE.MeshPhongMaterial({
    color: 0x999999,
    wireframe: false
  });
  material.shininess = 0;
  sphere = new THREE.Mesh(geometry, material);
  // sphere.castShadow = true;
  // sphere.receiveShadow = true;
  // scene.add(sphere);

  tempGeo = new THREE.Geometry();
  tempGeo.copy(geometry);
  temp = new THREE.Mesh(tempGeo, material);

  var pGeo = new THREE.PlaneGeometry(100, 100, 1, 1);
  plane = new THREE.Mesh(pGeo, material);
  plane.receiveShadow = true;
  plane.position.y = -radius - 3;
  plane.rotation.x = -90 * Math.PI / 180;
  scene.add(plane);
}

var initBoxes = function() {
  initedBoxes = true;
  var bufferGeometry = new THREE.BoxBufferGeometry(1, 1, 1);
  var geometry = new THREE.InstancedBufferGeometry();
  geometry.index = bufferGeometry.index;
  geometry.attributes.position = bufferGeometry.attributes.position;
  // geometry.attributes.normal = bufferGeometry.attributes.normal;

  // per instance data
  var offsets = [];
  var orientations = [];
  var colors = [];
  var vector = new THREE.Vector4();
  var x, y, z, w;
  var cscale = chroma.scale(['#ff0000', '#00ff00', '#0000ff']).classes(10);

  for (var i = 0; i < sphere.geometry.faces.length; i++) {
    center = getCenter(sphere.geometry.faces[i]);
    x = center.x;
    y = center.y;
    z = center.z;
    vector.set(x, y, z, 0).normalize();

    offsets.push(x + vector.x, y + vector.y, z + vector.z);

    // rotate

    rotation = getRotation(sphere.geometry.faces[i].normal);
    vector.copy(rotation).normalize();

    orientations.push(vector.x, vector.y, vector.z, vector.w);

    var color = chroma(cscale(THREE.Math.randFloat(0, 1))).brighten(1).hex();
    color = new THREE.Color(color);
    colors.push(color.r, color.g, color.b);
  }
  offsetAttribute = new THREE.InstancedBufferAttribute(new Float32Array(offsets), 3);
  orientationAttribute = new THREE.InstancedBufferAttribute(new Float32Array(orientations), 4);
  colorAttribute = new THREE.InstancedBufferAttribute(new Float32Array(colors), 3);

  geometry.addAttribute('offset', offsetAttribute);
  geometry.addAttribute('orientation', orientationAttribute);
  geometry.addAttribute('color', colorAttribute);

  var material = new THREE.ShaderMaterial({
    lights: true,
    uniforms: THREE.UniformsUtils.merge([
      THREE.UniformsLib["shadowmap"],
      THREE.UniformsLib["lights"],
      {
        lightPosition: {
          type: 'v3',
          value: light1.position
        },
        time: {
          type: 'f',
          value: 0
        }
      }
    ]),
    vertexShader: [
      'attribute vec3 offset;',
      'attribute vec4 orientation;',
      'attribute vec3 color;',

      'varying vec3 pos;',
      'varying vec3 vNormal;',
      'varying vec3 vWorldPosition;',
      'varying vec3 vColor;',

      'vec3 applyQuaternionToVector( vec4 q, vec3 v ){',
      'return v + 2.0 * cross( q.xyz, cross( q.xyz, v ) + q.w * v );',
      '}',

      THREE.ShaderChunk["common"],
      THREE.ShaderChunk["shadowmap_pars_vertex"],

      'void main() {',
      'vColor = color;',

      'vec3 vPosition = applyQuaternionToVector( orientation, position );',
      'pos = vPosition + offset;',

      'vNormal = normalMatrix * vec3(normal + normalize(offset) * 0.3);',

      'vec4 worldPosition = modelMatrix * vec4(pos, 1.0);',
      'vWorldPosition = worldPosition.xyz;',

      'gl_Position = projectionMatrix * modelViewMatrix * worldPosition;',
      THREE.ShaderChunk["shadowmap_vertex"],
      '}'
    ].join('\n'),
    fragmentShader: [
      THREE.ShaderChunk['common'],
      THREE.ShaderChunk['packing'],
      'varying vec3 pos;',
      'varying vec3 vNormal;',
      'varying vec3 vWorldPosition;',
      'varying vec3 vColor;',

      'uniform vec3 lightPosition;',

      THREE.ShaderChunk['shadowmap_pars_fragment'],
      'void main() {',
      'vec3 lightDirection = normalize(lightPosition + pos);',

      'float c = max(0.0, dot(vNormal, lightDirection)) * 2.;',
      // 'gl_FragColor = vec4(vColor.r + c , vColor.g + c , vColor.b + c , 1.);',
      'gl_FragColor = vec4(.3+c , .3+c , .3+c , 1.);',
      THREE.ShaderChunk['shadowmap_fragment'],
      '}'
    ].join('\n')
  });

  boxes = new THREE.Mesh(geometry, material);
  boxes.castShadow = true;
  boxes.receiveShadow = true;
  scene.add(boxes);
}

// find center position from 3 vertices
function getCenter(face) {
  var centroid = new THREE.Vector3(0, 0, 0);
  centroid.add(sphere.geometry.vertices[face.a]);
  centroid.add(sphere.geometry.vertices[face.b]);
  centroid.add(sphere.geometry.vertices[face.c]);
  centroid.divideScalar(3);
  return centroid;
}

function getRotation(normal) {
  var planeVector1 = new THREE.Vector3(0, 1, 0);
  var matrix1 = new THREE.Matrix4();
  var quaternion = new THREE.Quaternion().setFromUnitVectors(planeVector1, normal);
  var matrix = new THREE.Matrix4().makeRotationFromQuaternion(quaternion);
  var a = new THREE.Euler();
  a.setFromRotationMatrix(matrix, 'XYZ')
  // return a.toVector3();
  return quaternion;
}

// noise.seed(Math.random());
var update = function() {
  stats.update();
  var timer = (Date.now() - start) * .0002;

  // animate vertices of sphere with noise
  for (var i = 0; i < sphere.geometry.vertices.length; i++) {
    var p = sphere.geometry.vertices[i];
    var tp = temp.geometry.vertices[i];
    var n = noise.perlin3(tp.x / (10 - options.density) + timer, tp.y / (10 - options.density) + timer, tp.z / (10 - options.density) + timer) * options.scale;

    // move vertices with noise
    p.normalize().multiplyScalar(radius + n / 100);
  }

  sphere.geometry.verticesNeedUpdate = true;
  sphere.geometry.normalsNeedUpdate = true;
  sphere.geometry.computeVertexNormals();
  sphere.geometry.computeFaceNormals();


  // animate boxes
  if (initedBoxes) {
    for (var i = 0; i < sphere.geometry.faces.length; i++) {
      center = getCenter(sphere.geometry.faces[i]);
      x = center.x;
      y = center.y;
      z = center.z;
      offsetAttribute.setXYZ(i, center.x, center.y, center.z);

      rotation = getRotation(sphere.geometry.faces[i].normal);
      currentQ.copy(rotation).normalize();
      orientationAttribute.setXYZW(i, currentQ.x, currentQ.y, currentQ.z, currentQ.w);
    }
    offsetAttribute.needsUpdate = true;
    orientationAttribute.needsUpdate = true;
  }
}


var animate = function() {
  draw();
}

var render = function() {
  renderer.render(scene, camera);
}

init();
<html>

<head>
  <title>Instanced buffer test</title>
  <style>
    * {
      padding: 0px;
      margin: 0px;
    }
    
    html,
    body {
      overflow: hidden;
    }
  </style>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/106/three.min.js"></script>
  <script src="https://josephg.github.io/noisejs/perlin.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/chroma-js/2.0.3/chroma.min.js"></script>
  <script src="https://unpkg.com/[email protected]/examples/js/controls/OrbitControls.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js"></script>
</head>

<body>
</body>

</html>

Upvotes: 1

Views: 1187

Answers (1)

Rabbid76
Rabbid76

Reputation: 210938

If you want that the mesh casts proper shadow, it is not sufficient to use a ShaderMaterial for the .material property of the mesh.
The shadow of point lights depends on the .customDepthMaterial property. This means you've to write a shader (ShaderMaterial) which renders the object with the model transformations to the shadow map.


The shadow of the mesh is partially clipped by the near plane of the shadow camera.
Use a closer near plane (e.g. 0.1 rather than 0.5) by setting the .near property of the perspective shadow camera (light1.shadow.camera), to solve the issue:

light1 = new THREE.SpotLight( 0xffffff, 2, 200, 10 );
light1.position.set( -30, 30, 40 );
light1.castShadow = true;
light1.shadow.mapSize.x = 2048;
light1.shadow.mapSize.y = 2048;
light1.shadow.camera.near = 0.1;
scene.add(light1);

Further there are some issues in the shader. The statement

vec3 lightDirection = normalize(lightPosition + pos);

doesn't make any sense, because a direction is the vector from an point to another point, which is calculated by the (-)-operator (e.g. lightPosition - pos).
But that won't fix the issue, because lightPosition is a point in world space and pos is a point in model space.

You've to calculate the vector in view space, because in

float c = max(0.0, dot(vNormal, lightDirection)) * 2.;

vNormal is a vector in view space.

I recommend to calculate the light direction in the vertex shader (vLightDir) and to pass it to the fragment shader.
Calculate the direction in world space and transform it by the upper 3x3 of the view matrix (mat3(viewMatrix)):

vLightDir = mat3(viewMatrix) * (lightPosition - vWorldPosition);

Since worldPosition (as the name indicates) is is a position in world space, it has to be transformed by the viewMatrix rather than the modelViewMatrix:

gl_Position = projectionMatrix * viewMatrix * worldPosition;

Note, the modelViewMatrix transforms from object space to view space. The viewMatrix transforms from world space to view space. See also WebGLProgram .

Vertex shader:

vertexShader:
[
    'attribute vec3 offset;',
    'attribute vec4 orientation;',
    'attribute vec3 color;',

    'varying vec3 pos;',
    'varying vec3 vNormal;',
    'varying vec3 vWorldPosition;',
    'varying vec3 vColor;',
    'varying vec3 vLightDir;',

    'uniform vec3 lightPosition;',

    'vec3 applyQuaternionToVector( vec4 q, vec3 v ){',
        'return v + 2.0 * cross( q.xyz, cross( q.xyz, v ) + q.w * v );',
    '}',

    THREE.ShaderChunk["common"],
    THREE.ShaderChunk["shadowmap_pars_vertex"],

    'void main() {',
        'vColor = color;',

        'vec3 vPosition = applyQuaternionToVector( orientation, position );',
        'pos = vPosition + offset;',

        'vNormal = normalMatrix * vec3(normal + normalize(offset) * 0.3);',

        'vec4 worldPosition = modelMatrix * vec4(pos, 1.0);',
        'vWorldPosition = worldPosition.xyz;',

        'vLightDir = mat3(viewMatrix) * (lightPosition - vWorldPosition);',

        'gl_Position = projectionMatrix * viewMatrix * worldPosition;',
        THREE.ShaderChunk["shadowmap_vertex"],
    '}'
].join('\n')

Fragmetn shader:

fragmentShader:
[
    THREE.ShaderChunk['common'],
    THREE.ShaderChunk['packing'],
    'varying vec3 pos;',
    'varying vec3 vNormal;',
    'varying vec3 vWorldPosition;',
    'varying vec3 vColor;',
    'varying vec3 vLightDir;',

    THREE.ShaderChunk['shadowmap_pars_fragment'],
    'void main() {',
        'vec3 lightDirection = normalize(vLightDir);',

        'float c = max(0.0, dot(vNormal, lightDirection)) * 2.;',
        // 'gl_FragColor = vec4(vColor.r + c , vColor.g + c , vColor.b + c , 1.);',
        'gl_FragColor = vec4(.3+c , .3+c , .3+c , 1.);',
        THREE.ShaderChunk['shadowmap_fragment'],
    '}'
].join('\n')

var scene,camera,renderer;
var plane, temp, vnh, point;
var radius = 10;
var stats = new Stats();
var start = Date.now();
var options = {
    scale:200,
    density:2.5
}
var currentQ = new THREE.Quaternion();
var initedBoxes = false;

var init = function(){
    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
    camera.position.z = 25;
    var controls = new THREE.OrbitControls( camera );

    renderer = new THREE.WebGLRenderer();
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = THREE.PCFSoftShadowMap;
    document.body.appendChild( renderer.domElement );
    
    window.onresize = function() {
      renderer.setSize(window.innerWidth, window.innerHeight);
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
    }
    
    var gui = new dat.GUI({width: 300});
    gui.add(options,'scale').min(0).max(200).name('Scale');
    gui.add(options,'density').min(0).max(10).name('Density');

    // stats
    stats.showPanel(0);
    stats.domElement.style.position = 'fixed';
    stats.domElement.style.top = 0;
    document.body.appendChild( stats.domElement );

    initLights();
    initSphere();
    initBoxes();

    renderer.setAnimationLoop(function(){
        update();
        render();
    });
}

var initLights = function(){
    var ambientLight = new THREE.AmbientLight( 0x555555 );
    scene.add( ambientLight );
    light1 = new THREE.SpotLight( 0xffffff, 2, 200, 10 );
    light1.position.set( -30, 30, 40 );
    light1.castShadow = true;
    light1.shadow.mapSize.x = 2048;
    light1.shadow.mapSize.y = 2048;
    light1.shadow.camera.near = 0.1;
    scene.add(light1);

    light1Helper = new THREE.SpotLightHelper(light1, 0xffffff);
    scene.add(light1Helper);
}

var initSphere = function(){
    var geometry = new THREE.IcosahedronGeometry( radius, 3 );
    var material = new THREE.MeshPhongMaterial( {color: 0x999999, wireframe:false} );
    // var material = new THREE.ShaderMaterial({
    //     vertexShader: document.getElementById('vertexShader').innerHTML,
    //     fragmentShader: document.getElementById('fragmentShader').innerHTML
    // });
    material.shininess = 0;
    sphere = new THREE.Mesh( geometry, material );
    sphere.castShadow = true;
    sphere.receiveShadow = true;
    // scene.add(sphere);

    tempGeo = new THREE.Geometry();
    tempGeo.copy(geometry);
    temp = new THREE.Mesh( tempGeo, material );

    var pGeo = new THREE.PlaneGeometry(200, 200, 1, 1);
    plane = new THREE.Mesh( pGeo, material );
    plane.receiveShadow = true;
    plane.position.y = -radius - 10;
    plane.rotation.x = -90 * Math.PI/180;
    scene.add(plane);
}

var initBoxes = function(){
    initedBoxes = true;
    var bufferGeometry = new THREE.BoxBufferGeometry(1,1,1);
    var geometry = new THREE.InstancedBufferGeometry();
    geometry.index = bufferGeometry.index;
    geometry.attributes.position = bufferGeometry.attributes.position;
    // geometry.attributes.normal = bufferGeometry.attributes.normal;

    // per instance data
    var offsets = [];
    var orientations = [];
    var colors = [];
    var vector = new THREE.Vector4();
    var x, y, z, w;
    var cscale = chroma.scale(['#ff0000','#00ff00','#0000ff']).classes(10);

    for(var i=0; i<sphere.geometry.faces.length; i++){
        center = getCenter(sphere.geometry.faces[i]);
        x = center.x;
        y = center.y;
        z = center.z;
        vector.set( x, y, z, 0 ).normalize();

        offsets.push( x + vector.x, y + vector.y, z + vector.z );

        // rotate

        rotation = getRotation(sphere.geometry.faces[i].normal);
        vector.copy(rotation).normalize();

        orientations.push( vector.x, vector.y, vector.z, vector.w );

        var color = chroma(cscale(THREE.Math.randFloat(0,1))).brighten(1).hex();
        color = new THREE.Color(color);
        colors.push(color.r, color.g, color.b);
    }
    offsetAttribute = new THREE.InstancedBufferAttribute( new Float32Array( offsets ), 3 );
    orientationAttribute = new THREE.InstancedBufferAttribute( new Float32Array( orientations ), 4 );
    colorAttribute = new THREE.InstancedBufferAttribute( new Float32Array( colors ), 3 );

    geometry.addAttribute( 'offset', offsetAttribute );
    geometry.addAttribute( 'orientation', orientationAttribute );
    geometry.addAttribute( 'color', colorAttribute );

    var material = new THREE.ShaderMaterial( {
        lights: true,
        uniforms: THREE.UniformsUtils.merge([
            // THREE.UniformsLib["shadowmap"],
            THREE.UniformsLib["lights"],
            {
                lightPosition: {type: 'v3', value: light1.position},
                time: {type: 'f', value: 0}
            }
        ]),
        vertexShader:
        [
            'attribute vec3 offset;',
            'attribute vec4 orientation;',
            'attribute vec3 color;',

            'varying vec3 pos;',
            'varying vec3 vNormal;',
            'varying vec3 vWorldPosition;',
            'varying vec3 vColor;',
            'varying vec3 vLightDir;',
                                    
            'uniform vec3 lightPosition;',
    
            'vec3 applyQuaternionToVector( vec4 q, vec3 v ){',
                'return v + 2.0 * cross( q.xyz, cross( q.xyz, v ) + q.w * v );',
            '}',

            THREE.ShaderChunk["common"],
            THREE.ShaderChunk["shadowmap_pars_vertex"],

            'void main() {',
                'vColor = color;',

                'vec3 vPosition = applyQuaternionToVector( orientation, position );',
                'pos = vPosition + offset;',
                
                'vNormal = normalMatrix * vec3(normal + normalize(offset) * 0.3);',

                'vec4 worldPosition = modelMatrix * vec4(pos, 1.0);',
                'vWorldPosition = worldPosition.xyz;',

                'vLightDir = mat3(viewMatrix) * (lightPosition - vWorldPosition);',

                'gl_Position = projectionMatrix * viewMatrix * worldPosition;',
                THREE.ShaderChunk["shadowmap_vertex"],
            '}'
        ].join('\n')
        ,
        fragmentShader:
        [
            THREE.ShaderChunk['common'],
            THREE.ShaderChunk['packing'],
            'varying vec3 pos;',
            'varying vec3 vNormal;',
            'varying vec3 vWorldPosition;',
            'varying vec3 vColor;',
            'varying vec3 vLightDir;',

THREE.ShaderChunk['shadowmap_pars_fragment'],
            'void main() {',
                'vec3 lightDirection = normalize(vLightDir);',
                
                'float c = max(0.0, dot(vNormal, lightDirection)) * 2.;',
                // 'gl_FragColor = vec4(vColor.r + c , vColor.g + c , vColor.b + c , 1.);',
                'gl_FragColor = vec4(.3+c , .3+c , .3+c , 1.);',
                THREE.ShaderChunk['shadowmap_fragment'],
            '}'
        ].join('\n')
    });

    boxes = new THREE.Mesh( geometry, material );
    boxes.castShadow = true;
    boxes.receiveShadow = true;

    boxes.customDepthMaterial = new THREE.ShaderMaterial({
        vertexShader:
        [
            'attribute vec3 offset;',
            'attribute vec4 orientation;',

            'varying vec3 vWorldPosition;',

            'vec3 applyQuaternionToVector( vec4 q, vec3 v ){',
                'return v + 2.0 * cross( q.xyz, cross( q.xyz, v ) + q.w * v );',
            '}',

            'void main() {',
                'vec3 vPosition = applyQuaternionToVector( orientation, position );',
                'vec3 pos = vPosition + offset;',

                'vec4 worldPosition = modelMatrix * vec4(pos, 1.0);',
                'vWorldPosition = worldPosition.xyz;',
                
                'gl_Position = projectionMatrix * viewMatrix * worldPosition;',
            '}',
        ].join('\n')
        ,
        fragmentShader: THREE.ShaderLib.distanceRGBA.fragmentShader,
        uniforms: material.uniforms
    });
    scene.add(boxes);
}

// find center position from 3 vertices
function getCenter(face){
    var centroid = new THREE.Vector3(0,0,0);
    centroid.add(sphere.geometry.vertices[face.a]);
    centroid.add(sphere.geometry.vertices[face.b]);
    centroid.add(sphere.geometry.vertices[face.c]);
    centroid.divideScalar(3);
    return centroid;
}

function getRotation(normal){
    var planeVector1 = new THREE.Vector3(0,1,0);
    var matrix1 = new THREE.Matrix4();
    var quaternion = new THREE.Quaternion().setFromUnitVectors(planeVector1, normal);
    var matrix = new THREE.Matrix4().makeRotationFromQuaternion(quaternion);
    var a = new THREE.Euler( );
    a.setFromRotationMatrix ( matrix, 'XYZ' )
    // return a.toVector3();
    return quaternion;
}	

// noise.seed(Math.random());
var update = function(){
    stats.update();
    var timer = (Date.now() - start) * .0002;

    // animate vertices of sphere with noise
    for(var i=0; i<sphere.geometry.vertices.length; i++){
        var p = sphere.geometry.vertices[i];
        var tp = temp.geometry.vertices[i];
        var n = noise.perlin3(tp.x / (10-options.density) + timer, tp.y / (10-options.density)  + timer, tp.z / (10-options.density)  + timer) * options.scale;

        // move vertices with noise
        p.normalize().multiplyScalar(radius + n/100);
    }
    
    sphere.geometry.verticesNeedUpdate = true;
    sphere.geometry.normalsNeedUpdate = true;
    sphere.geometry.computeVertexNormals();
    sphere.geometry.computeFaceNormals();

    
    // animate boxes
    if(initedBoxes){
        for(var i=0; i<sphere.geometry.faces.length; i++){
            center = getCenter(sphere.geometry.faces[i]);
            x = center.x;
            y = center.y;
            z = center.z;
            offsetAttribute.setXYZ(i, center.x, center.y, center.z);

            rotation = getRotation(sphere.geometry.faces[i].normal);
            currentQ.copy(rotation).normalize();
            orientationAttribute.setXYZW( i, currentQ.x, currentQ.y, currentQ.z, currentQ.w );
        }
        offsetAttribute.needsUpdate = true;
        orientationAttribute.needsUpdate = true;
    }
}


var animate = function(){
    draw();
}

var render = function(){
    renderer.render( scene, camera );
}

init();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/106/three.min.js"></script>
<script src="https://josephg.github.io/noisejs/perlin.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chroma-js/2.0.3/chroma.min.js"></script>
<script src="https://unpkg.com/[email protected]/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js"></script>

Upvotes: 1

Related Questions