赞
踩
初始代码
- import "./style.css";
- import * as THREE from "three";
- import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
- import * as dat from "dat.gui";
- import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
-
- /**
- * Base
- */
- // Debug
- const gui = new dat.GUI();
-
- // Canvas
- const canvas = document.querySelector("canvas.webgl");
-
- // Scene
- const scene = new THREE.Scene();
- const dirLight = new THREE.DirectionalLight("#ffffff", 3);
- dirLight.position.set(0.25, 3, -2.25);
- scene.add(dirLight);
-
- /**
- * Test sphere
- */
- const testSphere = new THREE.Mesh(
- new THREE.SphereGeometry(1, 32, 32),
- new THREE.MeshStandardMaterial()
- )
- scene.add(testSphere)
-
-
-
-
- /**
- * Sizes
- */
- const sizes = {
- width: window.innerWidth,
- height: window.innerHeight,
- };
-
- window.addEventListener("resize", () => {
- // Update sizes
- sizes.width = window.innerWidth;
- sizes.height = window.innerHeight;
-
- // Update camera
- camera.aspect = sizes.width / sizes.height;
- camera.updateProjectionMatrix();
-
- // Update renderer
- renderer.setSize(sizes.width, sizes.height);
- renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
- });
-
- /**
- * Camera
- */
- // Base camera
- const camera = new THREE.PerspectiveCamera(
- 75,
- sizes.width / sizes.height,
- 0.1,
- 100
- );
- camera.position.set(4, 1, -4);
- scene.add(camera);
-
- // Controls
- const controls = new OrbitControls(camera, canvas);
- controls.enableDamping = true;
-
- /**
- * Renderer
- */
- const renderer = new THREE.WebGLRenderer({
- canvas: canvas,
- antialias: true,
- });
- renderer.setSize(sizes.width, sizes.height);
- renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
-
-
-
- /**
- * Animate
- */
- const tick = () => {
- // Update controls
- controls.update();
-
- // Render
- renderer.render(scene, camera);
-
- // Call tick again on the next frame
- window.requestAnimationFrame(tick);
- };
-
- tick();
初始效果:
- gui
- .add(dirLight, "intensity")
- .min(0)
- .max(10)
- .step(0.001)
- .name("lightIntensity");
- gui.add(dirLight.position, "x").min(-5).max(5).step(0.001).name("lightX");
- gui.add(dirLight.position, "y").min(-5).max(5).step(0.001).name("lightY");
- gui.add(dirLight.position, "z").min(-5).max(5).step(0.001).name("lightZ");
: Boolean 默认false ,物理的正确的照明方式
- const renderer = new THREE.WebGLRenderer({
- canvas: canvas,
- antialias: true,
- });
- renderer.setSize(sizes.width, sizes.height);
- renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
- renderer.physicallyCorrectLights=true
- const gltfLoader=new GLTFLoader()
- gltfLoader.load("/models/FlightHelmet/glTF/FlightHelmet.gltf", (gltf) => {
- const helmet = gltf.scene;
- helmet.scale.set(10, 10, 10);
- helmet.position.set(0, -4, 0);
- helmet.rotation.y = THREE.MathUtils.degToRad(90);
- scene.add(helmet);
- gui
- .add(helmet.rotation, "y")
- .min(-Math.PI)
- .max(Math.PI)
- .step(0.001)
- .name("rotation");
- });
渲染环境
- const cubeTextureLoader = new THREE.CubeTextureLoader();
- const envMap = cubeTextureLoader.load([
- "/textures/environmentMaps/0/px.jpg",
- "/textures/environmentMaps/0/nx.jpg",
- "/textures/environmentMaps/0/py.jpg",
- "/textures/environmentMaps/0/ny.jpg",
- "/textures/environmentMaps/0/pz.jpg",
- "/textures/environmentMaps/0/nz.jpg",
- ]);
- scene.background=envMap
callback - 以一个object3D对象作为第一个参数的函数。
通过此函数我们可以遍历加载的对象 从而分析出加载模型的类型
目的 在以后的每个分部分上可以添加阴影等其他效果
1.创建函数
-
- const updateAllMats = () => {
- scene.traverse((child) => {
- console.log(child)
-
- });
- };
2.调用函数
- gltfLoader.load("/models/FlightHelmet/glTF/FlightHelmet.gltf", (gltf) => {
- const helmet = gltf.scene;
- helmet.scale.set(10, 10, 10);
- helmet.position.set(0, -4, 0);
- helmet.rotation.y = THREE.MathUtils.degToRad(90);
- scene.add(helmet);
- updateAllMats()
- gui
- .add(helmet.rotation, "y")
- .min(-Math.PI)
- .max(Math.PI)
- .step(0.001)
- .name("rotation");
- });
通过判断类型可以给 mesh加入envmap材质
- const updateAllMats = () => {
- scene.traverse((child) => {
- console.log(child)
- if (
- child instanceof THREE.Mesh &&
- child.material instanceof THREE.MeshStandardMaterial
- ) {
- child.material.envMap = envMap;
- child.material.needsUpdate = true;
- child.castShadow = true;
- child.receiveShadow = true;
- }
- });
- };
加入后效果变化不是很明显
调节envMapIntensity可以看到效果
outputEncoding
属性控制输出渲染编码。默认情况下,outputEncoding的值为THREE.LinearEncoding
,看起来还行但是不真实,建议将值改为THREE.sRGBEncoding
- const renderer = new THREE.WebGLRenderer({
- canvas: canvas,
- antialias: true,
- });
- renderer.setSize(sizes.width, sizes.height);
- renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
- renderer.physicallyCorrectLights=true
- renderer.outputEncoding = THREE.sRGBEncoding
除此之外还有另一个属性值为THREE.GammaEncoding,这种编码的优点在于它允许我们使用一种表现像亮度brightness的叫gammaFactor的值。GammaEncoding是一种存储颜色的方法,根据人眼的敏感度优化明暗值的存储方式。当使用sRGBEncoding时,其实就像使用默认gammaFactor值为2.2的GammaEncoding。
下面链接可以提供更多信息关于GammaEncoding和sRGBEncodingColor management in three.js
https://medium.com/game-dev-daily/the-srgb-learning-curve-773b7f68cf7a
尽管这样就可能会有人认为GammaEncoding优于sRGBEncoding,因为我们可以在更暗或更亮的场景里控制gammaFactor,但是实际上这样做在物理层面上并不正确,下面会讲到如何更好管理亮度brightness
我们可以发现设置完渲染器的输出编码outputEncoding为THREE.sRGBEncoding后,我们的环境贴图颜色也改变了,虽然看起来效果不错,但我们还是要选择保留其原先正确的颜色。问题就在于我们设置完渲染器的输出编码之后,环境贴图的纹理还是默认的THREE.LinearEncoding。
其实规则很直接,所有我们能够直接看到的纹理贴图,比如map,就应该使用THREE.sRGBEncoding作为编码;而其他的纹理贴图比如法向纹理贴图normalMap就该使用THREE.LinearEncoding。
我们可以直接看到环境贴图,所以应该将其编码设为THREE.sRGBEncoding
renderer.toneMapping = THREE.ACESFilmicToneMapping
色调映射Tone mapping旨在将超高的动态范围HDR转换到我们日常显示的屏幕上的低动态范围LDR的过程。
说明一下HDR和LDR(摘自知乎LDR和HDR):因为不同的厂家生产的屏幕亮度(物理)实际上是不统一的,那么我们在说LDR时,它是一个0到1范围的值,对应到不同的屏幕上就是匹配当前屏幕的最低亮度(0)和最高亮度(1)
自然界中的亮度差异是非常大的。例如,蜡烛的光强度大约为15,而太阳光的强度大约为10w。这中间的差异是非常大的,有着超级高的动态范围。
我们日常使用的屏幕,其最高亮度是经过一系列经验积累的,所以使用、用起来不会对眼睛有伤害;但自然界中的,比如我们直视太阳时,实际上是会对眼睛产生伤害的。
那为了改变色调映射tone mapping,则要更新WebGLRenderer上的toneMapping属性,有以下这些值THREE.NoToneMapping (默认)
THREE.LinearToneMapping
THREE.ReinhardToneMapping
THREE.CineonToneMapping
THREE.ACESFilmicToneMapping
尽管我们的贴图不是HDR,但使用tone mapping可以塑造更真实的效果。
我们也可以通过GUI 来改变查看效果对比
- gui.add(renderer,"toneMapping",{
- No: THREE.NoToneMapping ,
- Linear:THREE.LinearToneMapping,
- Reinhard:THREE.ReinhardToneMapping,
- CineonTone:THREE.CineonToneMapping,
- ACESFilmicTone:THREE.ACESFilmicToneMapping
- }).onFinishChange(()=>{
- renderer.toneMapping=Number(renderer.toneMapping)
- })
但是这个效果只在环境上出现如果我们需要模型也相应的变化需要 在完成时调用updateAllMats
同时让材质开启更新
child.material.needsUpdate = true;
- const updateAllMats = () => {
- scene.traverse((child) => {
- console.log(child)
- if (
- child instanceof THREE.Mesh &&
- child.material instanceof THREE.MeshStandardMaterial
- ) {
- child.material.envMap = envMap;
- child.material.needsUpdate = true;
- child.material.envMapIntensity=debugObject.envMapIntensity
- child.castShadow = true;
- child.receiveShadow = true;
- }
- });
- };
- ....
-
- gui.add(renderer,"toneMapping",{
- No: THREE.NoToneMapping ,
- Linear:THREE.LinearToneMapping,
- Reinhard:THREE.ReinhardToneMapping,
- CineonTone:THREE.CineonToneMapping,
- ACESFilmicTone:THREE.ACESFilmicToneMapping
- }).onFinishChange(()=>{
- renderer.toneMapping=Number(renderer.toneMapping)
- updateAllMats()
- })
gui.add(renderer,"toneMappingExposure").min(0).max(10).step(0.01).name("toneMappingExposure")
当我们渲染的时候不开启 antialias
- const renderer = new THREE.WebGLRenderer({
- canvas: canvas,
- });
- const renderer = new THREE.WebGLRenderer({
- canvas: canvas,
- antialias: true,
- });
当开启之后
明显消除了锯齿化效果
- dirLight.castShadow=truedirLight.shadow.mapSize.set(1024,1024)
- dirLight.shadow.camera.far=15
- dirLight.shadow.normalBias=0.05
-
- ...
- ...
-
- const updateAllMats = () => {
- scene.traverse((child) => {
- console.log(child)
- if (
- child instanceof THREE.Mesh &&
- child.material instanceof THREE.MeshStandardMaterial
- ) {
- child.material.envMap = envMap;
- child.material.needsUpdate = true;
- child.material.envMapIntensity=debugObject.envMapIntensity
- child.castShadow = true;
- child.receiveShadow = true;
- }
- });
- };
-
- ...
- ...
-
- renderer.castShadow=true
- renderer.shadowMap.enabled = true;
- renderer.shadowMap.type = THREE.PCFSoftShadowMap;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。