Skip to content

threejs2

动力 IT 老师

查看版本

html
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Document</title>
		<script src="./three.js"></script>
	</head>
	<body>
		<script>
			console.log(window.__THREE__);
		</script>
	</body>
</html>

渲染第一个 threejs 三维对象

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./three.js"></script>
</head>
<body>
    <script>
        // console.log(window.__THREE__); // 135
        var scene = new THREE.Scene();
        var camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,1000);

        var render = new THREE.WebGLRenderer();
        render.setSize(window.innerWidth,window.innerHeight);
        document.querySelector("body").appendChild(render.domElement);

        var geometry = new THREE.BoxGeometry();
        var material = new THREE.MeshBasicMaterial({color:0x002299});
        var cube = new THREE.Mesh(geometry,material);
        scene.add(cube);

        camera.position.z = 5;
        cube.rotation.x += .8
        cube.rotation.y += .8

        render.render(scene,camera)
    </script>
</body>
</html>

坐标轴、光源、阴影效果

坐标轴

js
var axes = new THREE.AxesHelper(50)
scene.add(axes)

// 调整视角
camera.position.x = 5;
camera.position.y = 5;
camera.position.z = 5;
// 相机对准场景中心
camera.lookAt(scene.position)

光源

基础材质对各种光源是没有作用的。基础材质不需要光源就能显示,而非基础材质(lambert)则需要光源才能显示。

要设置接受阴影、产生阴影等。(可是不知道阴影为啥还是没出现plane.receiveShadow = true;原来是 receive 写错了)

html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./three.js"></script>
</head>

<body>
    <script>
        // console.log(window.__THREE__); // 135
        var scene = new THREE.Scene();
        var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

        var render = new THREE.WebGLRenderer();
        // ==========================
        render.shadowMap.enabled = true
        render.setSize(window.innerWidth, window.innerHeight);
        document.querySelector("body").appendChild(render.domElement);

        var geometry = new THREE.BoxGeometry(8, 8, 8);
        var material = new THREE.MeshLambertMaterial({ color: 0x002299 });
        var cube = new THREE.Mesh(geometry, material);
        // ==========================
        cube.castShadow = true;
        scene.add(cube);

        var planeGeometry = new THREE.PlaneGeometry(100, 100)
        var planeMaterial = new THREE.MeshLambertMaterial({ color: 0xcccccc });
        var plane = new THREE.Mesh(planeGeometry, planeMaterial);
        // ==========================
        plane.receiveShadow = true;
        plane.rotation.x = -0.5 * Math.PI
        plane.position.set(0, -4,0)
        scene.add(plane)

        camera.position.x = -30;
        camera.position.y = 45;
        camera.position.z = 35;
        camera.lookAt(scene.position)

        // 环境光
        var ambientLight = new THREE.AmbientLight(0xaaaaaa);
        scene.add(ambientLight)
        // 点光源
        var spotLight = new THREE.SpotLight(0xFFFFFF);
        spotLight.position.set(-60,40,-65);
        // ==========================
        spotLight.castShadow = true;
        spotLight.shadow.mapSize = new THREE.Vector2(1024,1024)
        spotLight.shadow.camera.far = 130;
        spotLight.shadow.camera.near = 40
        scene.add(spotLight)


        // 坐标系
        var axes = new THREE.AxesHelper(50)
        scene.add(axes)

        render.render(scene, camera)
    </script>
</body>

</html>

stats 库检测动画运行帧数

路径

shell
three.js-r135\examples\js\libs\stats.min.js

 import {Stats} from "../../libs/Stats/Stats.js"
js
            var stats = addStats();
            stats.update();

            function addStats(){
                var stats = new Stats();
                stats.domElement.style.position = 'absolute';
                stats.domElement.style.left = '0px';
                stats.domElement.style.top = '0px';
                stats.setMode(0);

                document.getElementById("myStats").appendChild(stats.domElement);
                return stats;
            }

dat.gui 库,方便测试动画。窗口缩放重新渲染

dat.gui 库介绍地址。

https://www.hangge.com/blog/cache/detail_1785.html

html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./default.css">
    <script src="./three.js"></script>
    <!-- stats 库,检测动画运行帧数 -->
    <!-- <script src="./stats.min.js"></script> -->
    
</head>

<body>
    <div id="myStats"></div>
    <script type="module">
        import {dat} from './dat.gui.js'
        import {Stats} from './Stats.js'
        // console.log(window.__THREE__); // 135
        var scene = new THREE.Scene();
        var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

        var render = new THREE.WebGLRenderer();
        render.shadowMap.enabled = true
        render.setSize(window.innerWidth, window.innerHeight);
        document.querySelector("body").appendChild(render.domElement);

        var geometry = new THREE.BoxGeometry(8, 8, 8);
        var material = new THREE.MeshLambertMaterial({ color: 0x002299 });
        var cube = new THREE.Mesh(geometry, material);
        cube.castShadow = true;
        scene.add(cube);

        var planeGeometry = new THREE.PlaneGeometry(100, 100)
        var planeMaterial = new THREE.MeshLambertMaterial({ color: 0xcccccc });
        var plane = new THREE.Mesh(planeGeometry, planeMaterial);
        plane.receiveShadow = true;
        plane.rotation.x = -0.5 * Math.PI
        plane.position.set(0, -4,0)
        scene.add(plane)

        camera.position.x = -30;
        camera.position.y = 45;
        camera.position.z = 35;
        camera.lookAt(scene.position)

        // 环境光
        var ambientLight = new THREE.AmbientLight(0xaaaaaa);
        scene.add(ambientLight)
        // 点光源
        var spotLight = new THREE.SpotLight(0xFFFFFF);
        spotLight.position.set(-60,40,-65);
        spotLight.castShadow = true;
        spotLight.shadow.mapSize = new THREE.Vector2(1024,1024)
        spotLight.shadow.camera.far = 130;
        spotLight.shadow.camera.near = 40
        scene.add(spotLight)


        // 坐标系
        var axes = new THREE.AxesHelper(50)
        scene.add(axes)


        var stats = addStats()

        var ctrlObj = {rotationSpeed:0.01,jumpSpeed:0.01};
        var ctrl = new dat.GUI();
        ctrl.add(ctrlObj,"rotationSpeed",0,1)
        ctrl.add(ctrlObj,"jumpSpeed",0,1);

        var gap = 0;
        renderScene();
        function renderScene(){
            // cube.rotation.x += 0.01;
            // cube.rotation.y += 0.01;
            // cube.rotation.z += 0.01;

            cube.rotation.x += ctrlObj.rotationSpeed;
            cube.rotation.y += ctrlObj.rotationSpeed;
            cube.rotation.z += ctrlObj.rotationSpeed;
            
            // gap += 0.01
            gap += ctrlObj.jumpSpeed
            cube.position.x = 25 + (20*(Math.sin(gap)))
            cube.position.y = 6 + (20*Math.abs(Math.cos(gap)))

            stats.update()
            requestAnimationFrame(renderScene);
            render.render(scene, camera)
        }

        function addStats(){
            var stats = new Stats();
            stats.domElement.style.position = 'absolute'
            stats.domElement.style.left = '10px'
            stats.domElement.style.top = '10px'
            stats.setMode(1);

            document.getElementById("myStats").appendChild(stats.domElement);
            return stats
        }

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

        function onWindowResize(){
            camera.aspect = window.innerWidth / window.innerHeight
            camera.updateProjectionMatrix();
            render.setSize(window.innerWidth,window.innerHeight)
        }
    </script>
</body>

</html>

threejs 场景的基本方法和属性

shell
#
scene.children.length 可以获取场景中的所有对象

#
根据name获取指定对象

cube.name = "cube"

var obj = scene.getObjectByName("cube",boolean) // 为true表示深度查找

#
scene.remove(obj) // 删除场景中的某个对象

# 
对场景中的每个对象执行一个回调方法
scene.traverse((obj)=>{

})

# 雾属性
scene.fog = new Fog(0xff0000,0.03,100);
or
scene.fog = new FogExp2(0x0000ff,0.02)

# overrideMaterial 属性
可以强制场景中的物体使用相同的属性

threejs 的几何体和网格

圆环几何体

js
var torusgeo = new TorusGeometry();
var torusmaterial = new MeshLambertMaterial({color:0xff2288});
var torus = new Mesh(torusgeo,torusmaterial);
torus.castShadow = true;

自定义几何体

js
var geometry = new BufferGeometry();
var vertices = new Float32Array([
    // 第一个面
    0,0,0,
    10,0,0,
    0,10,0,
    // ...

])
var attribute = new BufferAttribute(vertices)
geometry.attributes.position = attribute
var material = new MeshBasicMaterial({
    color:0x0000ff,
    side:DoubleSide
})
var mesh = new Mesh(geometry,material)
scene.add(mesh);

var wireFrame = new WireframeGeometry(geometry)
var line = new LineSegments(wireFrame);
line.material.depthTest = true;
line.material.transparent = false;
line.material.opacity = 0.5;
scene.add(line)

visible 属性设置是否可见

透视摄像机与正交投影摄像机

html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./default.css">
    <script src="./three.js"></script>
    <!-- stats 库,检测动画运行帧数 -->
    <!-- <script src="./stats.min.js"></script> -->

</head>

<body>
    <div id="myStats"></div>
    <script type="module">
        import { dat } from './dat.gui.js'
        import { Stats } from './Stats.js'
        // console.log(window.__THREE__); // 135
        var scene = new THREE.Scene();
        /**
         *1 我们能看到的视角
         *2 长宽比
         *3 表示距离摄像机多近的位置开始渲染
         *4 表示多远就不能渲染了
         */
        // var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        /**
         *1 渲染场景的左边界(边界越大,黑色的分割线越大)
         *2 渲染场景的右边界
         *3 渲染场景的上边界
         *4 渲染场景的下边界
         *5 近面距离,场景从距离相机这么近的点开始渲染
         *6 远面距离,场景从距离相机这么远的点停止渲染
         */
        var camera = new THREE.OrthographicCamera(window.innerWidth / -16, window.innerWidth / 16, window.innerHeight / 16, window.innerHeight / -16,-10,1024)

        var render = new THREE.WebGLRenderer();
        render.setSize(window.innerWidth, window.innerHeight);
        document.querySelector("body").appendChild(render.domElement);


        var planeGeometry = new THREE.PlaneGeometry(100, 100)
        var planeMaterial = new THREE.MeshLambertMaterial({ color: 0xcccccc });
        var plane = new THREE.Mesh(planeGeometry, planeMaterial);
        plane.rotation.x = -0.5 * Math.PI
        plane.position.set(0, -4, 0)
        scene.add(plane)

        camera.position.x = -40;
        camera.position.y = 55;
        camera.position.z = 45;
        camera.lookAt(scene.position)

        // 环境光
        var ambientLight = new THREE.AmbientLight(0xaaaaaa);
        scene.add(ambientLight)
        // 点光源
        var spotLight = new THREE.SpotLight(0xFFFFFF);
        spotLight.position.set(-60, 40, -65);
        // spotLight.castShadow = true;
        // spotLight.shadow.mapSize = new THREE.Vector2(1024,1024)
        // spotLight.shadow.camera.far = 130;
        // spotLight.shadow.camera.near = 40
        scene.add(spotLight)


        // 坐标系
        var axes = new THREE.AxesHelper(50)
        scene.add(axes)

        var stats = addStats()
        function addStats() {
            var stats = new Stats();
            stats.domElement.style.position = 'absolute'
            stats.domElement.style.left = '10px'
            stats.domElement.style.top = '10px'
            stats.setMode(1);

            document.getElementById("myStats").appendChild(stats.domElement);
            return stats
        }


        var ctrlObj = {
            showText: "透视投影相机",
            changeCamera() {
                if (camera instanceof THREE.OrthographicCamera) { // 透视
                    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
                    camera.position.x = -30;
                    camera.position.y = 45;
                    camera.position.z = 35;
                    camera.lookAt(scene.position)
                    ctrlObj.showText = "景深投影相机"
                } else {
                    camera = new THREE.OrthographicCamera(window.innerWidth / -16, window.innerWidth / 16, window.innerHeight / 16, window.innerHeight / -16)
                    camera.position.x = -30;
                    camera.position.y = 45;
                    camera.position.z = 35;
                    camera.lookAt(scene.position)
                    ctrlObj.showText = "透视投影相机"
                }
            }
        };
        var ctrl = new dat.GUI();
        ctrl.add(ctrlObj, "showText").listen() // listen 方法可以监听文本变化
        ctrl.add(ctrlObj, "changeCamera")

        var geometry = new THREE.BoxGeometry(8,8,8);
        var material = new THREE.MeshLambertMaterial({color:0xbbffaa})
        var cube1 = new THREE.Mesh(geometry,material)
        cube1.position.set(0,8,0)
        scene.add(cube1)

        for (var i = 0; i < (planeGeometry.parameters.width / 5); i++) {
            for (var j = 0; j < (planeGeometry.parameters.height / 5); j++) {
                var cubeGeo = new THREE.BoxGeometry(4, 4, 4)
                var cubeMaterial = new THREE.MeshLambertMaterial();
                cubeMaterial.color = new THREE.Color(0, Math.random() * 0.5 + 0.5, 0, 0)
                var cube = new THREE.Mesh(cubeGeo, cubeMaterial);
                cube.position.x = 5 * i + 5 / 2 - (planeGeometry.parameters.width / 2)
                cube.position.y = 3
                cube.position.z = 5 * j + 5 / 2 - (planeGeometry.parameters.height / 2)

                scene.add(cube)
            }
        }

        var pos = 0;
        renderScene();
        function renderScene() {
            pos += .1
            cube1.position.x = (10*Math.sin(pos))
            camera.lookAt(cube1.position)
            stats.update()
            requestAnimationFrame(renderScene);
            render.render(scene, camera)
        }



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

        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight
            camera.updateProjectionMatrix();
            render.setSize(window.innerWidth, window.innerHeight)
        }
    </script>
</body>

</html>

ambientLight 环境光源和 spotLight 聚光灯光源

html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./default.css">
    <script src="./three.js"></script>
    <!-- stats 库,检测动画运行帧数 -->
    <!-- <script src="./stats.min.js"></script> -->

</head>

<body>
    <div id="myStats"></div>
    <script type="module">
        import { dat } from './dat.gui.js'
        import { Stats } from './Stats.js'
        // console.log(window.__THREE__); // 135
        var scene = new THREE.Scene();

        var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.x = -40;
        camera.position.y = 140;
        camera.position.z = 45;
        camera.lookAt(scene.position)

        var geometry = new THREE.BoxGeometry(8, 8, 8);
        var material = new THREE.MeshLambertMaterial({ color: 0xbbffcc });
        var cube = new THREE.Mesh(geometry, material);
        cube.castShadow = true;
        scene.add(cube);

        var planeGeometry = new THREE.PlaneGeometry(100, 100)
        var planeMaterial = new THREE.MeshLambertMaterial({ color: 0xcccccc });
        var plane = new THREE.Mesh(planeGeometry, planeMaterial);
        plane.receiveShadow = true;
        plane.rotation.x = -0.5 * Math.PI
        plane.position.set(0, -4, 0)
        scene.add(plane)



        // 环境光
        // 参数一,光照颜色;参数二,光照强度。
        var ambientLight = new THREE.AmbientLight(0xffffff);
        // scene.add(ambientLight)
        // 点光源
        // 参数一,光照颜色;参数二,光照强度。
        // 参数三,光源发出光最大距离;参数四,光线散射角度。
        // 参数五,聚光锥的半影衰减百分比
        // 参数六,沿着光照距离的衰减量
        var spotLight = new THREE.SpotLight(0xFFFFFF);
        spotLight.castShadow = true;
        spotLight.shadow.mapSize = new THREE.Vector2(1024,1024)
        spotLight.shadow.camera.far = 130;
        spotLight.shadow.camera.near = 40
        spotLight.position.set(-60, 40, -65);
        scene.add(spotLight)


        // 坐标系
        var axes = new THREE.AxesHelper(50)
        scene.add(axes)
        // 光线辅助对象
        var lightHelper = new THREE.SpotLightHelper(spotLight);
        scene.add(lightHelper)
        // 相机辅助对象
        var shadowCameraHelper = new THREE.CameraHelper(spotLight.shadow.camera)
        scene.add(shadowCameraHelper)

        var ctrlObj = {
            // AmbientLight
            ambientIntensity:1, // 光照强度
            ambientLightColor:0xffffff, // 光照颜色
            // SpotLight
            spotLightIntensity:1,
            spotLightColor:0xcccccc,
            spotLightDistance:0,
            spotLightAngle:Math.PI / 3,
            spotPenumbra:0,
            spotDecay:0
        };
        var ctrl = new dat.GUI();
        // 为了区分,辅助对象设置分组方法
        var ambientLightFolder = ctrl.addFolder("ambientLight");
        // 将保存属性的对象添加到辅助对象 ctrl 中
        ambientLightFolder.add(ctrlObj,"ambientIntensity",0,5) // 设置范围,0, - 5
        // 会在控制菜单模块添加一个选项,在这个选项中能够直接传入颜色变量,然后通过 onChange 方法告诉控制菜单当每次改变颜色时执行传入的回调函数
        ambientLightFolder.addColor(ctrlObj,"ambientLightColor").onChange((crl)=>{
            ambientLight.color = new THREE.Color(crl)
        })
        var spotLightFolder = ctrl.addFolder("spotLight");
        spotLightFolder.add(ctrlObj,"spotLightIntensity",0,5);
        spotLightFolder.addColor(ctrlObj,"spotLightColor").onChange(crl=>{
            spotLight.color = new THREE.Color(crl)
        })
        spotLightFolder.add(ctrlObj,"spotLightDistance",0,1000).onChange(distance=>{
            spotLight.distance = distance
        })
        spotLightFolder.add(ctrlObj,"spotLightAngle",0,2*Math.PI).onChange(angle=>{
            spotLight.angle = angle
        })
        spotLightFolder.add(ctrlObj,"spotPenumbra",0,1).onChange(penumbra=>{
            spotLight.penumbra = penumbra
        })
        spotLightFolder.add(ctrlObj,"spotDecay",0,5).onChange(decay=>{
            spotLight.decay = decay
        })

        var render = new THREE.WebGLRenderer();
        render.shadowMap.enabled = true
        render.setSize(window.innerWidth, window.innerHeight);
        document.querySelector("body").appendChild(render.domElement);

        renderScene();
        function renderScene() {
            ambientLight.intensity = ctrlObj.ambientIntensity
            spotLight.intensity = ctrlObj.spotLightIntensity

            lightHelper.update()
            shadowCameraHelper.update()

            requestAnimationFrame(renderScene);
            render.render(scene, camera)
        }

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

        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight
            camera.updateProjectionMatrix();
            render.setSize(window.innerWidth, window.innerHeight)
        }
    </script>
</body>

</html>

PointLight 点光源和 DirectionalLight 平行光源

点光源不能产生阴影。

轨道控制对象

html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./default.css">
    <script src="./three.js"></script>
    <!-- stats 库,检测动画运行帧数 -->
    <!-- <script src="./stats.min.js"></script> -->

</head>

<body>
    <div id="myStats"></div>
    <script type="module">
        import { dat } from './dat.gui.js'
        import { Stats } from './Stats.js'
        import {GLTFLoader} from './GLTFLoader.js'
        import {OrbitControls} from './OrbitControls.js'
        // console.log(window.__THREE__); // 135
        var scene = new THREE.Scene();

        var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.x = -40;
        camera.position.y = 40;
        camera.position.z = 45;
        camera.lookAt(scene.position)


        var planeGeometry = new THREE.PlaneGeometry(100, 100)
        var planeMaterial = new THREE.MeshLambertMaterial({ color: 0xcccccc });
        var plane = new THREE.Mesh(planeGeometry, planeMaterial);
        plane.receiveShadow = true;
        plane.rotation.x = -0.5 * Math.PI
        plane.position.set(0, 0, 0)
        scene.add(plane)

        // 环境光
        // 参数一,光照颜色;参数二,光照强度。
        // var ambientLight = new THREE.AmbientLight(0xffffff);
        // scene.add(ambientLight)
        // 点光源
        // 光照颜色,光照强度,光源到光照强度为 0 的位置,沿着光照距离的衰退量
        // const pointLight = new THREE.PointLight(0xffffff,3,160)
        // pointLight.position.set(0,0,9)
        // scene.add(pointLight)
        // 平行光
        const directionalLight = new THREE.DirectionalLight(0xffffff,4)
        directionalLight.castShadow = true
        directionalLight.shadow.mapSize.width = 2048
        directionalLight.shadow.mapSize.height = 2048
        directionalLight.position.set(20,20,20)
        
        scene.add(directionalLight)

        const helper = new THREE.DirectionalLightHelper(directionalLight)
        scene.add(helper)

        // 相机辅助对象
        var shadowCameraHelper = new THREE.CameraHelper(directionalLight.shadow.camera)
        scene.add(shadowCameraHelper)

        var geometry = new THREE.BoxGeometry(1,1,1);
        var material = new THREE.MeshLambertMaterial({color:0xfa424242})
        var cube = new THREE.Mesh(geometry,material)
        cube.castShadow = true;
        cube.position.set(-20,2,0)
        directionalLight.target = cube
        scene.add(cube)

        // 在点光源的位置上放一个小球
        // var sphereGeo = new THREE.SphereGeometry(0.3)
        // var sphereMaterial = new THREE.MeshBasicMaterial({color:0xff0000});
        // var sphereMesh = new THREE.Mesh(sphereGeo,sphereMaterial);
        // sphereMesh.castShadow = true;
        // sphereMesh.position.copy(pointLight.position);
        // // sphereMesh.position.set(0,0,5)
        // scene.add(sphereMesh)

        // 坐标系
        var axes = new THREE.AxesHelper(50)
        scene.add(axes)

        var ctrlObj = {
          
        };
        var ctrl = new dat.GUI();

        var render = new THREE.WebGLRenderer();
        render.shadowMap.enabled = true
        render.setSize(window.innerWidth, window.innerHeight);
        document.querySelector("body").appendChild(render.domElement);

        // 轨道控制器对象
        var controls = new OrbitControls(camera,render.domElement)
        controls.update();

        loadModel()
        function loadModel(){
            new GLTFLoader().setPath("model/").
            load("untitled.glb",function(gltf){
                gltf.scene.scale.set(0.02,0.02,0.02);
                gltf.scene.traverse(obj=>{
                    if(obj.isMesh){
                        obj.castShadow = true;
                    }
                })
                scene.add(gltf.scene)
            })
        }

        renderScene();
        function renderScene() {
            controls.update();
            requestAnimationFrame(renderScene);
            render.render(scene, camera)
        }

        window.addEventListener('resize', onWindowResize, false);
        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight
            camera.updateProjectionMatrix();
            render.setSize(window.innerWidth, window.innerHeight)
        }
    </script>
</body>

</html>

HemisphereLight 半球光光源

可以为室外场景创建更加自然的光照

html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./default.css">
    <script src="./three.js"></script>
    <!-- stats 库,检测动画运行帧数 -->
    <!-- <script src="./stats.min.js"></script> -->

</head>

<body>
    <div id="myStats"></div>
    <script type="module">
        import { dat } from './dat.gui.js'
        import { Stats } from './Stats.js'
        import {GLTFLoader} from './GLTFLoader.js'
        import {OrbitControls} from './OrbitControls.js'
        // console.log(window.__THREE__); // 135
        var scene = new THREE.Scene();

        var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.x = -40;
        camera.position.y = 40;
        camera.position.z = 45;
        camera.lookAt(scene.position)


        var planeGeometry = new THREE.PlaneGeometry(100, 100)
        var planeMaterial = new THREE.MeshLambertMaterial({ color: 0xcccccc });
        var plane = new THREE.Mesh(planeGeometry, planeMaterial);
        plane.receiveShadow = true;
        plane.rotation.x = -0.5 * Math.PI
        plane.position.set(0, 0, 0)
        scene.add(plane)

        // 环境光
        // 参数一,光照颜色;参数二,光照强度。
        // var ambientLight = new THREE.AmbientLight(0xffffff);
        // scene.add(ambientLight)
        // 点光源
        // 光照颜色,光照强度,光源到光照强度为 0 的位置,沿着光照距离的衰退量
        // const pointLight = new THREE.PointLight(0xffffff,3,160)
        // pointLight.position.set(0,0,9)
        // scene.add(pointLight)
        // 平行光
        const directionalLight = new THREE.DirectionalLight(0xffffff,2)
        directionalLight.castShadow = true
        directionalLight.shadow.mapSize.width = 2048
        directionalLight.shadow.mapSize.height = 2048
        directionalLight.position.set(20,20,20)
        scene.add(directionalLight)
        // 平行光辅助对象
        const helper = new THREE.DirectionalLightHelper(directionalLight)
        scene.add(helper)
        // 相机辅助对象
        var shadowCameraHelper = new THREE.CameraHelper(directionalLight.shadow.camera)
        scene.add(shadowCameraHelper)

        // 1,天空中发出光线的颜色,默认值白色
        // 2,地面发出光线的颜色,默认值白色
        // 3,光照强度
        const hemisphereLight = new THREE.HemisphereLight(0xffffff)
        scene.add(hemisphereLight)

        var geometry = new THREE.BoxGeometry(1,1,1);
        var material = new THREE.MeshLambertMaterial({color:0xfa424242})
        var cube = new THREE.Mesh(geometry,material)
        cube.castShadow = true;
        cube.position.set(-20,2,0)
        // directionalLight.target = cube
        scene.add(cube)

        // 在点光源的位置上放一个小球
        // var sphereGeo = new THREE.SphereGeometry(0.3)
        // var sphereMaterial = new THREE.MeshBasicMaterial({color:0xff0000});
        // var sphereMesh = new THREE.Mesh(sphereGeo,sphereMaterial);
        // sphereMesh.castShadow = true;
        // sphereMesh.position.copy(pointLight.position);
        // // sphereMesh.position.set(0,0,5)
        // scene.add(sphereMesh)

        // 坐标系
        var axes = new THREE.AxesHelper(50)
        scene.add(axes)

        var ctrlObj = {
            hemisphereLightVisible:true,
            skyColor:0xffffff, // 改变此颜色,模型和阴影的颜色都发生了改变
            groundColor:0x00ff00,
            hemisphereIntensity:1
        };
        var ctrl = new dat.GUI();
        var hemisphereLightFolder = ctrl.addFolder("hemisphereLight")
        hemisphereLightFolder.add(ctrlObj,"hemisphereLightVisible").onChange(e=>{
            hemisphereLight.visible = e
        })
        hemisphereLightFolder.addColor(ctrlObj,"skyColor").onChange(e=>{
            hemisphereLight.color = new THREE.Color(e)
        })
        hemisphereLightFolder.addColor(ctrlObj,"groundColor").onChange(e=>{
            hemisphereLight.groundColor = new THREE.Color(e)
        })
        hemisphereLightFolder.add(ctrlObj,"hemisphereIntensity",0,10).onChange(e=>{
            hemisphereLight.intensity = e
        })

        var render = new THREE.WebGLRenderer();
        render.shadowMap.enabled = true
        render.setSize(window.innerWidth, window.innerHeight);
        document.querySelector("body").appendChild(render.domElement);

        // 轨道控制器对象
        var controls = new OrbitControls(camera,render.domElement)
        controls.update();

        loadModel()
        function loadModel(){
            new GLTFLoader().setPath("model/").
            load("untitled.glb",function(gltf){
                gltf.scene.scale.set(0.02,0.02,0.02);
                gltf.scene.traverse(obj=>{
                    if(obj.isMesh){
                        obj.castShadow = true;
                    }
                })
                scene.add(gltf.scene)
            })
        }

        renderScene();
        function renderScene() {
            controls.update();
            requestAnimationFrame(renderScene);
            render.render(scene, camera)
        }

        window.addEventListener('resize', onWindowResize, false);
        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight
            camera.updateProjectionMatrix();
            render.setSize(window.innerWidth, window.innerHeight)
        }
    </script>
</body>

</html>

平面光光源

从一个矩形平面上均匀地发射光线,这种光源的主要应用场景是模拟明亮的窗户或者条状灯光光源,实际在开发家具建模项目中会有广泛应用。

注意事项:

不支持阴影,只支持 MeshStandardMaterial 和 MeshPhysicalMaterial 两种材质。

必须在场景中加入 RectAreaLightUniformsLib,并调用 init()

html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./default.css">
    <script src="./three.js"></script>
    <!-- stats 库,检测动画运行帧数 -->
    <!-- <script src="./stats.min.js"></script> -->

</head>

<body>
    <div id="myStats"></div>
    <script type="module">
        import { dat } from './dat.gui.js'
        import { Stats } from './Stats.js'
        import {GLTFLoader} from './GLTFLoader.js'
        import {OrbitControls} from './OrbitControls.js'
        import {RectAreaLightUniformsLib} from './RectAreaLightUniformsLib.js'
        import {RectAreaLightHelper} from './RectAreaLightHelper.js'
        // console.log(window.__THREE__); // 135
        var scene = new THREE.Scene();

        var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.x = -40;
        camera.position.y = 40;
        camera.position.z = 45;
        camera.lookAt(scene.position)


        var planeGeometry = new THREE.PlaneGeometry(100, 100)
        var planeMaterial = new THREE.MeshLambertMaterial({ color: 0xcccccc });
        var plane = new THREE.Mesh(planeGeometry, planeMaterial);
        plane.receiveShadow = true;
        plane.rotation.x = -0.5 * Math.PI
        plane.position.set(0, 0, 0)
        scene.add(plane)

        // 平面光
        RectAreaLightUniformsLib.init();
        // 颜色,亮度,宽度,高度
        const rectLight = new THREE.RectAreaLight(0xff0000,5,4,10)
        rectLight.position.set(0,0,0)
        scene.add(rectLight)

        // 平面光辅助对象
        var rectAreaLightHelper = new RectAreaLightHelper(rectLight)
        scene.add(rectAreaLightHelper)

        // 接收平面光的地面
        const geoFloor = new THREE.BoxGeometry(2000,0.1,2000)
        const matStdFloor = new THREE.MeshStandardMaterial({color:0xffffff,roughness:0.2,metal:0})
        const mshStdFloor = new THREE.Mesh(geoFloor,matStdFloor)
        scene.add(mshStdFloor)

        // 坐标系
        var axes = new THREE.AxesHelper(50)
        scene.add(axes)

        var ctrlObj = {
            areaColor:0xff0000,
            areaIntensity:1
        };
        var ctrl = new dat.GUI();
        var areaLightFolder = ctrl.addFolder("AreaLight");
        areaLightFolder.addColor(ctrlObj,"areaColor").onChange(e=>{
            rectLight.color = new THREE.Color(e);
        })
        areaLightFolder.add(ctrlObj,"areaIntensity",0,10).onChange(e=>{
            rectLight.intensity = e
        })

        var render = new THREE.WebGLRenderer();
        render.shadowMap.enabled = true
        render.setSize(window.innerWidth, window.innerHeight);
        document.querySelector("body").appendChild(render.domElement);

        // 轨道控制器对象
        var controls = new OrbitControls(camera,render.domElement)
        controls.update();

        renderScene();
        function renderScene() {
            controls.update();
            requestAnimationFrame(renderScene);
            render.render(scene, camera)
        }

        window.addEventListener('resize', onWindowResize, false);
        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight
            camera.updateProjectionMatrix();
            render.setSize(window.innerWidth, window.innerHeight)
        }
    </script>
</body>

</html>

镜头光晕

Lensflare 镜头光晕并不是一种光源

html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./default.css">
    <script src="./three.js"></script>
    <!-- stats 库,检测动画运行帧数 -->
    <!-- <script src="./stats.min.js"></script> -->

</head>

<body>
    <div id="myStats"></div>
    <script type="module">
        import { dat } from './dat.gui.js'
        import { Stats } from './Stats.js'
        import {GLTFLoader} from './GLTFLoader.js'
        import {OrbitControls} from './OrbitControls.js'
        import {Lensflare,LensflareElement} from './Lensflare.js'
        // console.log(window.__THREE__); // 135
        var scene = new THREE.Scene();

        var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.x = -40;
        camera.position.y = 40;
        camera.position.z = 45;
        camera.lookAt(scene.position)

        var planeGeometry = new THREE.PlaneGeometry(100, 100)
        var planeMaterial = new THREE.MeshLambertMaterial({ color: 0xcccccc });
        var plane = new THREE.Mesh(planeGeometry, planeMaterial);
        plane.receiveShadow = true;
        plane.rotation.x = -0.5 * Math.PI
        plane.position.set(0, 0, 0)
        scene.add(plane)

        // 半球体光源
        // 1,天空中发出光线的颜色,默认值白色
        // 2,地面发出光线的颜色,默认值白色
        // 3,光照强度
        const hemisphereLight = new THREE.HemisphereLight(0xffffff)
        hemisphereLight.position.set(20,20,20)
        scene.add(hemisphereLight)

        // 加载贴图
        const textureLoader = new THREE.TextureLoader();
        const textureFlare0 = textureLoader.load("textures/lensflare0.png")
        const textureFlare1 = textureLoader.load("textures/lensflare1.png")

        // 创建一个镜头光晕对象
        const lensflare = new Lensflare();
        lensflare.addElement(new LensflareElement(textureFlare0,500,0))
        lensflare.addElement(new LensflareElement(textureFlare1,60,0.1))
        var lensflareEl
        lensflare.addElement(lensflareEl = new LensflareElement(textureFlare1,30,0.2))
        hemisphereLight.add(lensflare)

        // 坐标系
        var axes = new THREE.AxesHelper(50)
        scene.add(axes)

        var ctrlObj = {
            hemisphereLightVisible:true,
            skyColor:0xffffff, // 改变此颜色,模型和阴影的颜色都发生了改变
            groundColor:0x00ff00,
            hemisphereIntensity:1,
            // 光晕尺寸
            lensflareSize:30,
            lensflareDistance:0,
            lensflareColor:0xffffff
        };
        var ctrl = new dat.GUI();
        var hemisphereLightFolder = ctrl.addFolder("hemisphereLight")
        hemisphereLightFolder.add(ctrlObj,"hemisphereLightVisible").onChange(e=>{
            hemisphereLight.visible = e
        })
        hemisphereLightFolder.addColor(ctrlObj,"skyColor").onChange(e=>{
            hemisphereLight.color = new THREE.Color(e)
        })
        hemisphereLightFolder.addColor(ctrlObj,"groundColor").onChange(e=>{
            hemisphereLight.groundColor = new THREE.Color(e)
        })
        hemisphereLightFolder.add(ctrlObj,"hemisphereIntensity",0,10).onChange(e=>{
            hemisphereLight.intensity = e
        })
        var lensflareFolder = ctrl.addFolder("lensflare")
        lensflareFolder.add(ctrlObj,"lensflareSize",0,100).onChange(e=>{
            lensflareEl.size = e
        })
        lensflareFolder.add(ctrlObj,"lensflareDistance",0,1).onChange(e=>{
            lensflareEl.distance = e
        })
        lensflareFolder.addColor(ctrlObj,"lensflareColor").onChange(e=>{
            lensflareEl.color = new THREE.Color(e)
        })

        var render = new THREE.WebGLRenderer();
        render.shadowMap.enabled = true
        render.setSize(window.innerWidth, window.innerHeight);
        document.querySelector("body").appendChild(render.domElement);

        // 轨道控制器对象
        var controls = new OrbitControls(camera,render.domElement)
        controls.update();

        loadModel()
        function loadModel(){
            new GLTFLoader().setPath("model/").
            load("untitled.glb",function(gltf){
                gltf.scene.scale.set(0.02,0.02,0.02);
                gltf.scene.traverse(obj=>{
                    if(obj.isMesh){
                        obj.castShadow = true;
                    }
                })
                scene.add(gltf.scene)
            })
        }

        renderScene();
        function renderScene() {
            controls.update();
            requestAnimationFrame(renderScene);
            render.render(scene, camera)
        }

        window.addEventListener('resize', onWindowResize, false);
        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight
            camera.updateProjectionMatrix();
            render.setSize(window.innerWidth, window.innerHeight)
        }
    </script>
</body>

</html>

内置平面几何体

js
            var planeGeo = new THREE.PlaneGeometry(4,8,4,8);
            var planeMat = new THREE.MeshBasicMaterial({color:0xff0000});
            planeMat.side = THREE.DoubleSide;
            planeMat.wireframe = true;
            var plane = new THREE.Mesh(planeGeo,planeMat);
            scene.add(plane);

内置常见普通几何体

查看代码

凸面几何体和车削几何体

高级几何体,threejs 不包含,需要额外导入。

凸面几何体,是有很多凸起的几何体。

车削几何体,是后二维图形“平移”得到的几何体。

管道几何体

先绘制一条曲线,再根据这条曲线生成管道

拉伸几何体

将二维几何体拉伸成三维几何体

Parametric Geometry 参数化缓冲几何体

TextGeometry 文本缓冲几何体

材质常用基础属性

Released under the MIT License.