three.js shader十行代码实现游戏场景中的动态道路

1,280 阅读3分钟

游戏场景中的动态道路

今天在shadertoy上看到了一个效果,是个游戏的道路场景,十分亲切。一看代码10行?UC震惊部来了。太牛了,真的10行,佩服,直呼看不懂。

不过不影响我们拿来用,恩,移植到three中跑了一下,完美实现。

image.png

image.png

image.png

Kapture 2023-09-26 at 180.32.31.gif


移植流程

1. 导入 Three.js

在使用 Three.js 之前,我们需要先导入 Three.js 库。可以通过以下代码进行导入:

import * as THREE from 'three';

2. 创建场景和相机

在 Three.js 中,我们需要先创建一个场景和一个相机。可以通过以下代码进行创建:

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, _w / _h, 1, 1000);
camera.position.z = 112;
const vertexShader = `
        varying vec2 vUv;
        void main(){
        vUv = uv;
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
    `;

let fragmentShader = `
    uniform float iTime;
    varying vec2 vUv;
    void main() {
        vec2 p = vUv;
        vec3 q=vec3(1.0,1.0,1.0),d=vec3(p-.5*q.xy,q.y)/q.y,c=vec3(0,.5,.7);
        q=d/(.1-d.y);
        float a=iTime, k=sin(.2*a), w = q.x *= q.x-=.05*k*k*k*q.z*q.z;
        vec3 col = vec3(0);
        col.xyz=d.y>.04?c:
            sin(4.*q.z+40.*a)>0.?
            w>2.?c.xyx:w>1.2?d.zzz:c.yyy:
            w>2.?c.xzx:w>1.2?c.yxx*2.:(w>.004?c:d).zzz;
        gl_FragColor = vec4(col,1.0);
    }
    `;

let width = 160;
const geometry = new THREE.PlaneGeometry(width,width*_b)

const material = new THREE.ShaderMaterial({
    uniforms: {
        iTime: { value: 0 },
    },
    fragmentShader: fragmentShader,
    vertexShader: vertexShader,
});

const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

const renderer = new THREE.WebGLRenderer();
renderer.setSize(_w,_h);

// new OrbitControls(camera, renderer.domElement);

renderer.setClearColor(0xFFFFFF, 1.0);
document.body.appendChild(renderer.domElement);

let speed = 0.01;
function animate() {
    requestAnimationFrame(animate);
    material.uniforms.iTime.value += speed;
    renderer.render(scene, camera);
}
animate();

let div = document.getElementById("speed");

div.innerHTML = `速度:${speed*3000 | 0}`
window.addEventListener('keydown',(event)=>{
    switch (event.key) {
        case 'w':
         speed += 0.0005;
         break;
        case 's':
         speed -= 0.0005;
         break;
         case ' ':
         speed = 0 ;
         break;
    }
    div.innerHTML = `速度:${speed*3000 | 0}`
})

在这里,我们使用了透视相机 PerspectiveCamera,并设置了视角 fov、宽高比 aspect、近截面 near 和远截面 far。然后,我们将相机的位置设置为 (0, 0, 5),也就是在场景中的坐标为 (0, 0, 0) 的位置后面。

3. 创建渲染器

接下来,我们需要创建一个渲染器,用于将场景渲染到屏幕上。在这里,我们创建了一个 WebGL 渲染器 WebGLRenderer,并将它的大小设置为窗口大小。然后,我们将渲染器的 DOM 元素添加到了页面中。

4. 创建几何体和材质

在 Three.js 中,我们需要将几何体和材质结合起来,才能形成一个完整的物体。可以通过以下代码进行创建.在这里,我们创建了一个平面几何体 PlaneGeometry,并使用了基础网格材质 ShaderMaterial,将它们结合起来形成了一个物体。然后,我们将这个物体添加到了场景中。

5. 渲染场景

最后,我们需要在动画循环中渲染场景。可以通过以下代码进行循环.我们使用了 requestAnimationFrame 函数来进行动画循环,并在每次循环中旋转物体 cube,然后使用渲染器 renderer 渲染场景 scene 和相机 camera


代码是shader写的,核心代码如下。

uniform float iTime;
varying vec2 vUv;
void main() {
    vec2 p = vUv;
    vec3 q=vec3(1.0,1.0,1.0),d=vec3(p-.5*q.xy,q.y)/q.y,c=vec3(0,.5,.7);
    q=d/(.1-d.y);
    float a=iTime, k=sin(.2*a), w = q.x *= q.x-=.05*k*k*k*q.z*q.z;
    vec3 col = vec3(0);
    col.xyz=d.y>.04?c:
        sin(4.*q.z+40.*a)>0.?
        w>2.?c.xyx:w>1.2?d.zzz:c.yyy:
        w>2.?c.xzx:w>1.2?c.yxx*2.:(w>.004?c:d).zzz;
    gl_FragColor = vec4(col,1.0);
}

道路.png

为了增加趣味性,我添加了三个按键,加速、减速、归零。在线体验地址,为了体验我固定了宽高。

参考文章:www.extentofthejam.com/pseudo/espa…