THREE.WebGLRenderer
的参数设置
THREE.WebGLRenderer
对象的参数设置非常影响渲染效果
要想获得比较好的渲染效果,对THREE.WebGLRenderer
可进行以下参数设置。
var renderer;
renderer = new THREE.WebGLRenderer({
antialias:true
});
renderer.physicallyCorrectLights = true;
renderer.setPixelRatio( window.devicePixelRatio * 2);
renderer.setSize(window.innerWidth,window.innerHeight);
renderer.gammaOutput = true;
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.0;
renderer.outputEncoding = THREE.sRGBEncoding;
document.body.appendChild(renderer.domElement);
上述设置中,
抗锯齿
renderer = new THREE.WebGLRenderer({
antialias:true,
//alpha:true
});
将antialias
设置为true表示开启抗锯齿。
物理灯光
renderer.physicallyCorrectLights = true;
表示将启用正确的物理灯光。
像素采样率
renderer.setPixelRatio( window.devicePixelRatio * 2);
表示使用2倍设备像素点采样,这有利于消除锯齿获得更加高质量的效果。
阴影贴图
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
上述设置表示启用阴影贴图,并将阴影贴图类型设置为THREE.PCFSoftShadowMap
,其中不同阴影贴图类型的设置如下:
THREE.BasicShadowMap
:能够给出没有经过过滤的阴影映射,速度最快,但质量最差THREE.PCFShadowMap
:默认值,使用Percentage-Closer Filtering (PCF)
算法来过滤阴影映射THREE.PCFSoftShadowMap
:和PCFShadowMap
一样使用Percentage-Closer Filtering (PCF)
算法过滤阴影映射,但在使用低分辨率阴影图时具有更好的软阴影THREE.VSMShadowMap
:使用Variance Shadow Map (VSM)
算法来过滤阴影映射。当使用VSMShadowMap
时,所有阴影接收者也将会投射阴影
色彩映射
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.0;
设置这个属性主要是为了在普通计算机显示器或者移动设备屏幕等低动态范围介质上,模拟、逼近高动态范围(HDR)效果。
这个属性如果要生效就是需要将hdr贴图作为场景的环境贴图,获取IBL的光照效果。使用hdr贴图作为场景的环境贴图也将大幅度提升场景或者模型的渲染效果。
其中toneMapping
算法可选择:
THREE.NoToneMapping
THREE.LinearToneMapping
THREE.ReinhardToneMapping
THREE.CineonToneMapping
THREE.ACESFilmicToneMapping
这里我们使用的是效果最好的ACES
算法,当然你也可以自定义自己的tonemapping
算法,如何自定义可参考官方示例。
而toneMappingExposure
表示曝光级别,值越大曝光程度越高,场景中的光线越充足,模型就越亮。
渲染器输出编码
renderer.outputEncoding = THREE.sRGBEncoding;
设置渲染器的输出编码,默认的输出编码为THREE.LinearEncoding
,我们这里设置为THREE.sRGBEncoding
。
调整贴图的encoding
three.js
将贴图的编码都默认设置为THREE.LinearEncoding
,导致图片色彩失真(色彩不像正常那么鲜艳,会灰蒙蒙的),所以务必将场景中的所有贴图的编码都调整为THREE.sRGBEncoding
。
贴图修改encoding
const textureLoader = new THREE.TextureLoader();
textureLoader.load( "./model_D.png", function(texture){
texture.encoding = THREE.sRGBEncoding;
});
环境贴图修改encoding
const hdrUrl = './res/hdr/ballroom_1k.hdr'
new THREE.RGBELoader().load(hdrUrl, texture => {
const gen = new THREE.PMREMGenerator(renderer)
envMap = gen.fromEquirectangular(texture).texture;
envMap.encoding = THREE.sRGBEncoding;
scene.environment = envMap;
scene.background = envMap;
texture.dispose();
gen.dispose();
})
场景中的光照调整
固定光源
环境光 AmbientLight
环境光会均匀的照亮场景中的所有物体,环境光不能产生阴影,因为环境光没有方向。
const light = new THREE.AmbientLight( 0x404040 ); // soft white light
scene.add( light );
半球光 HemisphereLight
半球光同样也是照亮场景中的所有物体,并且光照颜色会从天空光照颜色慢慢的渐变到地面光照颜色。
const light = new THREE.HemisphereLight( 0xffffbb, 0x080820, 1 );
scene.add( light );
平行光,定向光 DirectionalLight
平行光是沿着特定方向发射的光。这种光的表现像是无限远,发出的平行光线,常常用来模拟太阳光。
平行光可以产生阴影,所以我们可以对平行光的阴影进行更多的设置。
const directional_light = new THREE.DirectionalLight( 0xffffff, 1 );
directional_light.position.set( 0, 1, 0 );
directional_light.castShadow = true;
scene.add( directional_light );
directional_light.shadow.mapSize.width = 512 * 2;
directional_light.shadow.mapSize.height = 512 * 2;
directional_light.shadow.bias = 0.05;
directional_light.shadow.normalBias = 0.05;
directional_light.shadow.camera.near = 0.5;
directional_light.shadow.camera.far = 500;
点光源 PointLight
点光源为为一个点向各个方向发射的光源,可以理解为一个灯泡发出的光。
点光源同样也可以产生阴影,所以我们也可以对点光源的阴影进行更多设置。
const point_light = new THREE.PointLight( 0xffffff, 1, 100 );
point_light.position.set( 0, 10, 4 );
point_light.castShadow = true;
scene.add( point_light );
point_light.shadow.mapSize.width = 512 * 2;
point_light.shadow.mapSize.height = 512 * 2;
point_light.shadow.bias = 0.05;
point_light.shadow.normalBias = 0.05;
point_light.shadow.camera.near = 0.5;
point_light.shadow.camera.far = 500;
聚光灯 SpotLight
聚光灯光线从一个点沿一个方向射出,随着光线照射的变远,光线圆锥体的尺寸也逐渐增大。
聚光灯同样也可以产生阴影,所以我们也可以对点光源的阴影进行更多设置。
const spot_light = new THREE.SpotLight( 0xffffff );
spot_light.position.set( 100, 1000, 100 );
spot_light.castShadow = true;
scene.add( spot_light );
spot_light.shadow.mapSize.width = 512 * 2;
spot_light.shadow.mapSize.height = 512 * 2;
spot_light.shadow.camera.near = 500;
spot_light.shadow.camera.far = 4000;
spot_light.shadow.camera.fov = 30;
区域光 RectAreaLight
区域光不支持阴影,因此没有更多的阴影设置。
const width = 10;
const height = 10;
const intensity = 1;
const rect_light = new THREE.RectAreaLight( 0xffffff, intensity, width, height );
rect_light.position.set( 5, 5, 0 );
rect_light.lookAt( 0, 0, 0 );
scene.add( rect_light )
BL(基于图像的光照)
基于图像的光照(Image based lighting, IBL)通过一张环境贴图(Environment Map)来保存物体周围的环境信息,并通过一系列处理模拟真实环境中的光照效果。IBL与固定光源相比能够提供更加真实的光照效果,所以如果你用固定光源得到的效果很差,可以使用一张好的hdr作为场景的环境贴图,相信渲染效果会更加真实。
这里我们一般使用hdr作为我们的环境贴图,找好符合场景渲染要求的hdr环境贴图,然后设置场景的toneMapping,
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.0;
然后加载hdr环境贴图,如下:
const hdrUrl = './res/hdr/ballroom_1k.hdr'
new THREE.RGBELoader().load(hdrUrl, texture => {
const gen = new THREE.PMREMGenerator(renderer)
envMap = gen.fromEquirectangular(texture).texture;
envMap.encoding = THREE.sRGBEncoding;
envMap.material = THREE.EquirectangularReflectionMapping;
scene.environment = envMap;
scene.background = envMap;
texture.dispose();
gen.dispose();
})
然后修改场景中所有的mesh的材质的环境贴图
scene.traverse(child => {
if (child instanceof THREE.Mesh) {
child.material.envMap = envMap;
child.material.envMapIntensity = envMapIntensity;
child.material.needsUpdate = true;
child.castShadow = true;
child.receiveShadow = true;
}
})
加载模型的设置
在加载模型时也需要对模型进行一些设置才能有好的效果,如何设置可参考以下代码
new THREE.GLTFLoader().load('./example.glb', result => {
model = result.scene || result.scenes[0];
model.traverse(child => {
if ( child.isMesh ) {
child.castShadow = true;
child.receiveShadow = true;
if(child.material.map) {
child.material.map.encoding = THREE.sRGBEncoding;
child.material.map.anisotropy = 1;
}
if (child.material.emissiveMap) {
child.material.emissiveMap.encoding = THREE.sRGBEncoding;
}
if (child.material.map || child.material.emissiveMap) {
child.material.needsUpdate = true;
}
if(envMap) {
child.material.envMap = envMap;
child.material.envMapIntensity = 1;
}
}
})
})
在加载模型完成之后,需要遍历模型的所有网格,并将所有网格材质的贴图的encoding修改为THREE.sRGBEncoding
,然后设置所有网格材质贴图的环境贴图以及强度。
三维模型
建议使用glb格式的模型