bufferGeometry and shadow react-three-fiber

Viewed 1442

I am trying to make my own bufferGeometry work in react-three-fiber, and I am just not getting any shadows. I think I'm doing everything fine with the vertices, normals and even uvs of my bufferGeometry. I also tried to add indices to faces but for some reason that is messing up my geometry as well.

If I put a small test box in the scene, it cast shadows. Only my bufferGeometry doesn't seem to work.

Here is the code:

//MyGeometry.js -- it is stripe of quadrilaterals

import * as THREE from 'three'
import {useMemo} from 'react'

export default function MyGeometry({ position, L, W, res }) {

    const [vertices, faces, normals, uvs] = useMemo( () => 
    {
        const arr = [];
        const facesArr = [];
        const normsArr = [];
        const uvArr = []
        const triangle_pairs = Math.floor(L/res);
        const upper_left_corner = [
            position[0] - W/2,
            position[1] - L/2,
            position[2]
        ];

        for(let i=0; i<triangle_pairs; i++) {

            //v0
            const v0x = upper_left_corner[0] + Math.random()*(res/3);
            const v0y = upper_left_corner[1] + i*res + Math.random()*(res/3);
            const v0z = upper_left_corner[2] + Math.random()*(res/3);
            const v0 = new THREE.Vector3(v0x, v0y, v0z); //for computing normals
            //v1
            const v1x = upper_left_corner[0] + W + Math.random()*(res/3);
            const v1y = upper_left_corner[1] + i*res + Math.random()*(res/3);
            const v1z = upper_left_corner[2] + Math.random()*(res/3);
            const v1 = new THREE.Vector3(v1x, v1y, v1z); //for computing normals
            //v2
            const v2x = upper_left_corner[0] + Math.random()*(res/3);
            const v2y = upper_left_corner[1] + (i+1)*res + Math.random()*(res/3);
            const v2z = upper_left_corner[2] + Math.random()*(res/3);
            const v2 = new THREE.Vector3(v2x, v2y, v2z); //for computing normals
            //v3
            const v3x = upper_left_corner[0] + W + Math.random()*(res/3);
            const v3y = upper_left_corner[1] + (i+1)*res + Math.random()*(res/3);
            const v3z = upper_left_corner[2] + Math.random()*(res/3);
            const v3 = new THREE.Vector3(v3x, v3y, v3z); //for computing normals


            //UPPER triangle - 0,1,2

            //positions

            arr.push(v0x, v0y, v0z, v1x, v1y, v1z, v2x, v2y, v2z);

            //face, or indices

            facesArr.push(2*i, 2*i+1, 2*i+2);

            //normals

            const s21 = new THREE.Vector3();
            s21.copy(v2).sub(v1);
            const s01 = new THREE.Vector3();
            s01.copy(v0).sub(v1);
            const n1 = new THREE.Vector3();
            n1.copy(s21).cross(s01).normalize();
            normsArr.push(n1.x, n1.y, n1.z);
            normsArr.push(n1.x, n1.y, n1.z);
            normsArr.push(n1.x, n1.y, n1.z);

            //uvs
            uvArr.push(0,0, 1,0, 0,1)

            //LOWER triangle - 2,1,3
            
            //positions

            arr.push(v2x, v2y, v2z, v1x, v1y, v1z, v3x, v3y, v3z);

            //face, or indices

            facesArr.push(2*i+2, 2*i+1, 2*i+3);

            //normals

            const s31 = new THREE.Vector3();
            s31.copy(v3).sub(v1);
            const n2 = new THREE.Vector3();
            n2.copy(s31).cross(s21).normalize();
            normsArr.push(n2.x, n2.y, n2.z);
            normsArr.push(n2.x, n2.y, n2.z);
            normsArr.push(n2.x, n2.y, n2.z);

            //uvs
            uvArr.push(0,1, 1,0, 1,1);

        }        

        const vertices = new Float32Array(arr);
        const faces = new Uint16Array(facesArr);
        const normals = new Float32Array(normsArr);
        const uvs = new Float32Array(uvs);

        return [vertices, faces, normals, uvs];

    } 
    , []);

    return (
      <mesh castShadow={true} receiveShadow={true} position={position}>

        <bufferGeometry 
            attach="geometry"
        >
            <bufferAttribute
                attachObject={["attributes", "position"]}
                count={vertices.length / 3}
                array={vertices}
                itemSize={3}
            />

            <bufferAttribute
                attachObject={["attributes", "normal"]}
                count={normals.length / 3}
                array={normals}
                itemSize={3}
            />

            <bufferAttribute
                attachObject={["attributes", "uv"]}
                count={uvs.length / 2}
                array={uvs}
                itemSize={2}
            />
            
            //faces - for some reason if i uncomment this my geometry gets messed up
            {/* <bufferAttribute
                attach="index"
                count={faces.length}
                array={faces}
                itemSize={1}
            /> */}

        </bufferGeometry>

        <meshPhongMaterial attach="material" color="blue" />

      </mesh>
    )
}

and then it gets rendered in here

//Main.js

import { useRef, useState, useEffect } from 'react'
import { Canvas, useFrame, useThree } from 'react-three-fiber'
import { Box } from '@react-three/drei'
import MyCamera from '../three/MyCamera'
import MyGeometry from '../three/MyGeometry'


export default function Main() { 

  return (
    <div className="canvasContainer">
    <Canvas 
      linear = "true"
      frameloop="demand" 
      shadows = "true"
      shadowMap
    >
      <MyCamera position={[0, 5, 30]} infLimit={-200} supLimit ={200} />
      <ambientLight intensity={1}/>
      <mesh receiveShadow castShadow position={[0,0,0]}>
        <planeBufferGeometry attach="geometry" args={[1000, 1000]} />
        <meshStandardMaterial attach="material" color="gray" />
      </mesh>
      <MyGeometry position={[0, 0, 0]} L={10} W={5} res={10} />
    </Canvas>
    </div>
  )
}
1 Answers

TURNS out I resolved the issue:

  1. I was really stupid when creating my mesh. I forgot in the Canvas object the y axis is flipped, so my normals were all messed up.
  2. Theres a shadow camera property in lights witch has to contain the region the shadow will be rendered. I had no idea about that and also had to change things a bit:

I had a light in MyCamera object witch I deleted and instead create a light inside Main functional component and to access this shadow camera stuff I had to create it imperatively inside useMemo and render as a primitive inside Canvas:

export default function Main() { 

  const dirLight = useMemo(()=>{
  
    const light = new THREE.DirectionalLight('white');
    light.castShadow=true;
    //Set up shadow properties for the light
    light.shadow.mapSize.width = 10240 // default
    light.shadow.mapSize.height = 10240 // default
    light.shadow.camera.near = 0.1 // default
    light.shadow.camera.far = 5000 // default
    light.shadow.camera.top = -100 // default
    light.shadow.camera.right = 100 // default
    light.shadow.camera.left = -100 // default
    light.shadow.camera.bottom = 100 // default
    return light
  
  },[])

  return (
    <div className="canvasContainer">
    <Canvas 
      linear = "true"
      frameloop="demand" 
      shadows = "true"
      shadowMap
    >
      
      <MyCamera position={[0, 0, 30]} infLimit={-1000} supLimit ={0} />
      
      <ambientLight intensity={0.2}/>

      <primitive object={dirLight} position={[30, 0, 30]} />
      <primitive object={dirLight.target} position={[0, 0, 0]}  />
      
      <mesh receiveShadow position={[0,0,-2]}>
        <planeBufferGeometry attach="geometry" args={[1000, 1000]} />
        <meshStandardMaterial attach="material" color="gray" />
      </mesh>
      
      <MyGeometry position={[0, 0, 0]} L={400} W={5} res={4} />

    </Canvas>
    </div>
  )
}

And then

Related