ThreeJS锯齿以及抗锯齿

1,716 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第10天,点击查看活动详情

场景

当这个立方体的场景中,虽然看到立方体是正常的场景,但是仔细放大的时候,发现立方体的直线没有水平或者垂直。如图下:

微信截图_20221215142147.png

1.锯齿

1.1抗锯齿(AA)

微信截图_20221215142645.png

根据上图,发现这些直线还是没有完全水平或垂直。所以我们采用**抗锯齿(AA)**的技术来解决这个问题。

我们首先可以通过向WebGLRenderer构造函数传递一个新参数来启用抗锯齿,然后采用antialias:true来开启抗锯齿,来编写开启抗锯齿。

function createRenderer(){ //添加开启抗锯齿 
const renderer = new WebGLRenderer({antialias:true}); 
renderer.physicallyCorrectLights = true; 
return renderer; 
}

开启抗锯齿之后,效果图

微信截图_20221215144829.png

1.2多重采样抗锯齿(MSAA)

其实抗锯齿就是内置的WebGL方法执行的。MSAA不是优先的解决方案,就算你启用了抗锯齿(AA),有一些场景还是显示锯齿。但是有些场景为多个立方体、很多线条的场景中是很难消除混叠的。不只是这个MSAA,也有其他抗锯齿技术,比如:SMAA、FXAA等。

2.处理自动调整浏览器的窗口大小

2.1侦听resize浏览器窗口上的事件

我们需要某种方式来监听浏览器,所以我们想要侦听调整大小事件。可以采用resize事件来触发,就是说当它们调整浏览器窗口大小时,resize事件将触发。

任何HTML元素上的各种事件,例如说click、scroll、keypress等等。

(1)当用户点击的时候,click事件将触发。

(2)当用户旋转滚轮的时候,scroll事件将触发。

(3)当用户调整浏览器窗口大小的时候,resize事件将触发。

注:resize事件侦听器必须要附加到全局window对象

测试:addEventListener和resize事件在浏览器控制台中来测试

function onResize(){ 
    console.log('You resized the browser window!');
} 
window.addEventListener('resize',onResize);

可以把这些以上的代码粘贴到浏览器控制台。

当在测试中,用鼠标拖动窗口来调整浏览器的大小都会导致resize事件来触发。

微信截图_20221215145422.png

当调整窗口大小的时候,发现onResize()方法会被多次调用。但是resize事件已触发十次或更多次,可以考虑使用loadsh之_.throttle类的节流函数来防止过于频繁地调用。

2.2扩展Resizer类

首先在加载时,设置初始大小,然后在调整浏览器的大小发生变化时再次调用。因此,在我们的场景加载时调用一次:

在Resizer.js文件里,把自动调整大小代码到setSize函数中并在加载时调用它,设置事件监听器。

const setSize = (container,camera,renderer)=>{   
    // 设置相机的宽高比   
    camera.aspect = container.clientWidth / container.clientHeight;   
    // 更新相机的截锥   
    camera.updateProjectionMatrix();   
    // 更新渲染器和画布的大小   
    renderer.setSize(container.clientWidth,container.clientHeight);   
    // 设置像素比率(用于移动设备)   
    renderer.setPixelRatio(window.devicePixelRatio);
};
class Resizer{     
    constructor(container,camera,renderer){       
        // 在负载上设置初始大小       
        setSize(container,camera,renderer);
        // 添加一个事件侦听器,并setSize在事件触发时再次调用。     
        window.addEventListener('resize',()=>{        
        // 如果发生大小调整,请再次设置大小    
        setSize(container,camera,renderer);  
    });    
    } 
} 
export { Resizer };

当调整窗口大小的时候,立方体就会变形,包括压扁和拉伸,效果图:

微信截图_20221215145828.png

微信截图_20221215145849.png

其实它只有在画布中绘制了一个帧,当画布被调整大小时,那么立方体就会变形。

2.3创建一个onResize钩子

当每一次触发调整大小事件时我们都需要生成一个新的帧。我们需要调用的是Workd.render之后setSize,在事件监听器Resizer类。要避免整个World类传递给Resizer。我们将创建一个Resizer.onResize钩子。

class Resizer { 
    constructor(container, camera, renderer){  
    // 在负载上设置初始大小 
    setSize(container, camera, renderer); 
    windows.addEventListener('resize',()=>{ 
        //如果发生大小调整,请再次设置大小 
        setSize(container, camera, renderer); 
        //执行任何自定义操作 
        this.onResize(); 
        }); 
    } 
    onResize(){} 
}

其实.onResize是一个空方法,我们可以从Resizer类外部自定义。

2.4Resizer.onResize在世界中自定义

在World.js中,.onResize方法用一个新的调用World.render。

onstructor(container){ 
    camera = createCamera();
    scene = createScene(); 
    renderer = createRenderer();
    container.append(renderer.doElement);
    const cube = crateCube(); 
    const light = createLights();
    scene.add(cube, light);
    const resizer = new Resizer(container, camera, renderer);
    //添加调用World.render 
    resizer.onResize = () =>{ 
        this.render(); 
    } 
}

这样自动调整大小就完成了。

3.练习

微信截图_20221215152422.png (1)和(2)为合并,需要处理启用和禁用抗锯齿,看看它们有什么不同?

在WebGLRenderer构造函数中(antinalias)把true改为false。

//启用抗锯齿 
const renderer = new WebGLRenderer({antinalias:true});

以上的代码例子,启用了抗锯齿,来看看这个效果图:

微信截图_20221215152013.png

可以看到立方体边缘出现有直线或垂直。

//禁用抗锯齿 
const renderer = new WebGLRenderer({antinalias:false})

以上的代码例子,禁用了抗锯齿,来看看这个效果图:

微信截图_20221215152209.png

根据上面的两个图,发现立方体边缘还是有所不同的,该图的立方体边缘是没有直线或者垂直。

(3)和(4)首先把部分代码改为注释。

// 设置调整大小及onResize钩子     
resizer.onResize = () =>{        
    this.render();      
};

当随着浏览器的窗口调整大小,就会发现立方体就会变形,拉伸或缩短,效果图:

微信截图_20221215152330.png