three.js流动道路

此前探索过流动线的实现方式,看到另一个案例,学习了另一种实现流动道路的方法

用到了一个开源计算库

https://github.com/yszhao91/xtorcga,实现效果

 

核心就是基于 cga.extrudeToGeometryBuffer ,将道路生成两份,然后不断改变贴图的位置即可

 

前序逻辑都是在做一些数据处理,后续有需要再研究数据结构吧。

json文件可从此处取得

https://github.com/a873054267/threeStudy/blob/master/04%E9%AB%98%E7%BA%A7%E5%87%A0%E4%BD%95%E4%BD%93/cs.json

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="../js/three.min.js"></script>
    <script src="../js/OrbitControls.js"></script>
    <script src="../js/Init.js"></script>
    <script src="jquery-1.10.2.min.js"></script>

    <style>
        #box{
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
        }
    </style>

</head>
<body>
<div class="ys-absolute-container" id="box" style="overflow: hidden"></div>
<script src="../js/plugins/MeshLine.js"></script>
<script src="cga.js"></script>

<script>
    let el=document.getElementById('box')
    let app=new THREE.inithree(el,{
        axes:true,
    })
    let scene=app.scene
    let renderer=app.renderer
    let camera=app.camera
    let controls=app.controls

    camera.position.set(50, 50, 50)
    camera.lookAt(new THREE.Vector3(0, 0, 0))

    var ambientLight = new THREE.AmbientLight(0xffffff,0.9);
    scene.add(ambientLight);

    // 参数依次为color、强度、距离、角度,penumbra,decay
    var spotLight = new THREE.SpotLight(0x00ff00);
    spotLight.position.set(30,25,-10)
    scene.add(spotLight);

    function toSVGData(segments, points) {
        var str = "";
        var pos = 0
        for (let i = 0; i < segments.length; i++)
        {
            switch (segments[i])
            {
                case 1:
                    str += "M " + points[pos].x + " " + points[pos].y + " ";
                    pos += 1;
                    break;
                case 2:
                    str += "L " + points[pos].x + " " + points[pos].y + " ";
                    pos += 1;
                    break;
                case 3:
                    str += "Q " + points[pos].x + " " + points[pos].y + " "
                        + points[pos + 1].x + " " + points[pos + 1].y + " ";
                    pos += 2;
                    break;
                case 4:
                    str += "C " + points[pos].x + " " + points[pos].y + " "
                        + points[pos + 1].x + " " + points[pos + 1].y + " "
                        + points[pos + 2].x + " " + points[pos + 2].y + " ";
                    pos += 3;
                    break;
                case 5:
                    str += "Z ";
                    break;
                default:
                    break;
            }

        }

        return str;
    }
    function parseFloats(string) {

        var array = string.split(/[\s,]+|(?=\s?[+\-])/);

        for (var i = 0; i < array.length; i++)
        {

            var number = array[i];

            // Handle values like 48.6037.7.8
            // TODO Find a regex for this

            if (number.indexOf('.') !== number.lastIndexOf('.'))
            {

                var split = number.split('.');

                for (var s = 2; s < split.length; s++)
                {

                    array.splice(i + s - 1, 0, '0.' + split[s]);

                }

            }

            array[i] = parseFloat(number);

        }

        return array;


    }
    function parsePathNode(data) {

        var path = new THREE.ShapePath();

        var point = new THREE.Vector2();
        var control = new THREE.Vector2();

        var firstPoint = new THREE.Vector2();
        var isFirstPoint = true;
        var doSetFirstPoint = false;
        window.cga=cga

        var d = data;

        // console.log( d );

        var commands = d.match(/[a-df-z][^a-df-z]*/ig);

        for (var i = 0, l = commands.length; i < l; i++)
        {

            var command = commands[i];

            var type = command.charAt(0);
            var data = command.substr(1).trim();

            if (isFirstPoint === true)
            {

                doSetFirstPoint = true;
                isFirstPoint = false;

            }

            switch (type)
            {

                case 'M':
                    var numbers = parseFloats(data);
                    for (var j = 0, jl = numbers.length; j < jl; j += 2)
                    {

                        point.x = numbers[j + 0];
                        point.y = numbers[j + 1];
                        control.x = point.x;
                        control.y = point.y;

                        if (j === 0)
                        {

                            path.moveTo(point.x, point.y);

                        } else
                        {

                            path.lineTo(point.x, point.y);

                        }

                        if (j === 0 && doSetFirstPoint === true) firstPoint.copy(point);

                    }
                    break;

                case 'H':
                    var numbers = parseFloats(data);

                    for (var j = 0, jl = numbers.length; j < jl; j++)
                    {

                        point.x = numbers[j];
                        control.x = point.x;
                        control.y = point.y;
                        path.lineTo(point.x, point.y);

                        if (j === 0 && doSetFirstPoint === true) firstPoint.copy(point);

                    }
                    break;

                case 'V':
                    var numbers = parseFloats(data);

                    for (var j = 0, jl = numbers.length; j < jl; j++)
                    {

                        point.y = numbers[j];
                        control.x = point.x;
                        control.y = point.y;
                        path.lineTo(point.x, point.y);

                        if (j === 0 && doSetFirstPoint === true) firstPoint.copy(point);

                    }
                    break;

                case 'L':
                    var numbers = parseFloats(data);

                    for (var j = 0, jl = numbers.length; j < jl; j += 2)
                    {

                        point.x = numbers[j + 0];
                        point.y = numbers[j + 1];
                        control.x = point.x;
                        control.y = point.y;
                        path.lineTo(point.x, point.y);

                        if (j === 0 && doSetFirstPoint === true) firstPoint.copy(point);

                    }
                    break;

                case 'C':
                    var numbers = parseFloats(data);

                    for (var j = 0, jl = numbers.length; j < jl; j += 6)
                    {

                        path.bezierCurveTo(
                            numbers[j + 0],
                            numbers[j + 1],
                            numbers[j + 2],
                            numbers[j + 3],
                            numbers[j + 4],
                            numbers[j + 5]
                        );
                        control.x = numbers[j + 2];
                        control.y = numbers[j + 3];
                        point.x = numbers[j + 4];
                        point.y = numbers[j + 5];

                        if (j === 0 && doSetFirstPoint === true) firstPoint.copy(point);

                    }
                    break;

                case 'S':
                    var numbers = parseFloats(data);

                    for (var j = 0, jl = numbers.length; j < jl; j += 4)
                    {

                        path.bezierCurveTo(
                            getReflection(point.x, control.x),
                            getReflection(point.y, control.y),
                            numbers[j + 0],
                            numbers[j + 1],
                            numbers[j + 2],
                            numbers[j + 3]
                        );
                        control.x = numbers[j + 0];
                        control.y = numbers[j + 1];
                        point.x = numbers[j + 2];
                        point.y = numbers[j + 3];

                        if (j === 0 && doSetFirstPoint === true) firstPoint.copy(point);

                    }
                    break;

                case 'Q':
                    var numbers = parseFloats(data);

                    for (var j = 0, jl = numbers.length; j < jl; j += 4)
                    {

                        path.quadraticCurveTo(
                            numbers[j + 0],
                            numbers[j + 1],
                            numbers[j + 2],
                            numbers[j + 3]
                        );
                        control.x = numbers[j + 0];
                        control.y = numbers[j + 1];
                        point.x = numbers[j + 2];
                        point.y = numbers[j + 3];

                        if (j === 0 && doSetFirstPoint === true) firstPoint.copy(point);

                    }
                    break;

                case 'T':
                    var numbers = parseFloats(data);

                    for (var j = 0, jl = numbers.length; j < jl; j += 2)
                    {

                        var rx = getReflection(point.x, control.x);
                        var ry = getReflection(point.y, control.y);
                        path.quadraticCurveTo(
                            rx,
                            ry,
                            numbers[j + 0],
                            numbers[j + 1]
                        );
                        control.x = rx;
                        control.y = ry;
                        point.x = numbers[j + 0];
                        point.y = numbers[j + 1];

                        if (j === 0 && doSetFirstPoint === true) firstPoint.copy(point);

                    }
                    break;

                case 'A':
                    var numbers = parseFloats(data);

                    for (var j = 0, jl = numbers.length; j < jl; j += 7)
                    {

                        var start = point.clone();
                        point.x = numbers[j + 5];
                        point.y = numbers[j + 6];
                        control.x = point.x;
                        control.y = point.y;
                        parseArcCommand(
                            path, numbers[j], numbers[j + 1], numbers[j + 2], numbers[j + 3], numbers[j + 4], start, point
                        );

                        if (j === 0 && doSetFirstPoint === true) firstPoint.copy(point);

                    }
                    break;

                case 'm':
                    var numbers = parseFloats(data);

                    for (var j = 0, jl = numbers.length; j < jl; j += 2)
                    {

                        point.x += numbers[j + 0];
                        point.y += numbers[j + 1];
                        control.x = point.x;
                        control.y = point.y;

                        if (j === 0)
                        {

                            path.moveTo(point.x, point.y);

                        } else
                        {

                            path.lineTo(point.x, point.y);

                        }

                        if (j === 0 && doSetFirstPoint === true) firstPoint.copy(point);

                    }
                    break;

                case 'h':
                    var numbers = parseFloats(data);

                    for (var j = 0, jl = numbers.length; j < jl; j++)
                    {

                        point.x += numbers[j];
                        control.x = point.x;
                        control.y = point.y;
                        path.lineTo(point.x, point.y);

                        if (j === 0 && doSetFirstPoint === true) firstPoint.copy(point);

                    }
                    break;

                case 'v':
                    var numbers = parseFloats(data);

                    for (var j = 0, jl = numbers.length; j < jl; j++)
                    {

                        point.y += numbers[j];
                        control.x = point.x;
                        control.y = point.y;
                        path.lineTo(point.x, point.y);

                        if (j === 0 && doSetFirstPoint === true) firstPoint.copy(point);

                    }
                    break;

                case 'l':
                    var numbers = parseFloats(data);

                    for (var j = 0, jl = numbers.length; j < jl; j += 2)
                    {

                        point.x += numbers[j + 0];
                        point.y += numbers[j + 1];
                        control.x = point.x;
                        control.y = point.y;
                        path.lineTo(point.x, point.y);

                        if (j === 0 && doSetFirstPoint === true) firstPoint.copy(point);

                    }
                    break;

                case 'c':
                    var numbers = parseFloats(data);

                    for (var j = 0, jl = numbers.length; j < jl; j += 6)
                    {

                        path.bezierCurveTo(
                            point.x + numbers[j + 0],
                            point.y + numbers[j + 1],
                            point.x + numbers[j + 2],
                            point.y + numbers[j + 3],
                            point.x + numbers[j + 4],
                            point.y + numbers[j + 5]
                        );
                        control.x = point.x + numbers[j + 2];
                        control.y = point.y + numbers[j + 3];
                        point.x += numbers[j + 4];
                        point.y += numbers[j + 5];

                        if (j === 0 && doSetFirstPoint === true) firstPoint.copy(point);

                    }
                    break;

                case 's':
                    var numbers = parseFloats(data);

                    for (var j = 0, jl = numbers.length; j < jl; j += 4)
                    {

                        path.bezierCurveTo(
                            getReflection(point.x, control.x),
                            getReflection(point.y, control.y),
                            point.x + numbers[j + 0],
                            point.y + numbers[j + 1],
                            point.x + numbers[j + 2],
                            point.y + numbers[j + 3]
                        );
                        control.x = point.x + numbers[j + 0];
                        control.y = point.y + numbers[j + 1];
                        point.x += numbers[j + 2];
                        point.y += numbers[j + 3];

                        if (j === 0 && doSetFirstPoint === true) firstPoint.copy(point);

                    }
                    break;

                case 'q':
                    var numbers = parseFloats(data);

                    for (var j = 0, jl = numbers.length; j < jl; j += 4)
                    {

                        path.quadraticCurveTo(
                            point.x + numbers[j + 0],
                            point.y + numbers[j + 1],
                            point.x + numbers[j + 2],
                            point.y + numbers[j + 3]
                        );
                        control.x = point.x + numbers[j + 0];
                        control.y = point.y + numbers[j + 1];
                        point.x += numbers[j + 2];
                        point.y += numbers[j + 3];

                        if (j === 0 && doSetFirstPoint === true) firstPoint.copy(point);

                    }
                    break;

                case 't':
                    var numbers = parseFloats(data);

                    for (var j = 0, jl = numbers.length; j < jl; j += 2)
                    {

                        var rx = getReflection(point.x, control.x);
                        var ry = getReflection(point.y, control.y);
                        path.quadraticCurveTo(
                            rx,
                            ry,
                            point.x + numbers[j + 0],
                            point.y + numbers[j + 1]
                        );
                        control.x = rx;
                        control.y = ry;
                        point.x = point.x + numbers[j + 0];
                        point.y = point.y + numbers[j + 1];

                        if (j === 0 && doSetFirstPoint === true) firstPoint.copy(point);

                    }
                    break;

                case 'a':
                    var numbers = parseFloats(data);

                    for (var j = 0, jl = numbers.length; j < jl; j += 7)
                    {

                        var start = point.clone();
                        point.x += numbers[j + 5];
                        point.y += numbers[j + 6];
                        control.x = point.x;
                        control.y = point.y;
                        parseArcCommand(
                            path, numbers[j], numbers[j + 1], numbers[j + 2], numbers[j + 3], numbers[j + 4], start, point
                        );

                        if (j === 0 && doSetFirstPoint === true) firstPoint.copy(point);

                    }
                    break;

                case 'Z':
                case 'z':
                    path.currentPath.autoClose = true;

                    if (path.currentPath.curves.length > 0)
                    {

                        // Reset point to beginning of Path
                        point.copy(firstPoint);
                        path.currentPath.currentPoint.copy(point);
                        isFirstPoint = true;

                    }
                    break;

                default:
                    console.warn(command);

            }

            // console.log( type, parseFloats( data ), parseFloats( data ).length  )

            doSetFirstPoint = false;

        }

        return path;

    }

    $.ajax({url:"cs.json",success:function(data){

            var paths = [data.d[25],
                data.d[26],
                data.d[27]];
            var strs = []
            for (let i = 0; i < paths.length; i++)
            {
                const node = paths[i];
                var pts = node.p.points.__a
                var sgs = node.p.segments.__a
                var str = toSVGData(sgs, pts)
                strs.push(str)
            }



            var roadMaterial = new THREE.MeshBasicMaterial({
                transparent: true,
                depthWrite: false
            })

            const textureLoader = new THREE.TextureLoader();
            let url='../images/traffic_01.png'
            var roadMap = textureLoader.load(url);
            roadMap.wrapS = THREE.RepeatWrapping
            roadMap.wrapT = THREE.RepeatWrapping
            roadMap.rotation = Math.PI / 2;
            roadMap.offset.set(0.5, 0.5);
            var roadMap1 = textureLoader.load(url);
            roadMap1.wrapS = THREE.RepeatWrapping
            roadMap1.wrapT = THREE.RepeatWrapping
            roadMap1.rotation = Math.PI / 2;
            roadMap1.offset.set(0.5, 0.5);
            var roadMap2 =textureLoader.load(url);
            roadMap2.wrapS = THREE.RepeatWrapping
            roadMap2.wrapT = THREE.RepeatWrapping
            roadMap2.rotation = Math.PI / 2;
            roadMap2.offset.set(0.5, 0.5);

            var roadmats = [roadMaterial, roadMaterial.clone(), roadMaterial.clone()]
            roadmats[0].map = roadMap;
            roadmats[1].map = roadMap1;
            roadmats[2].map = roadMap2;

            var roadGroup = new THREE.Group();
            roadGroup.position.set(66.94476, 0, -235.22057);
            var roaddata = [{
                "position": {
                    "x": 66.94476,
                    "y": -235.22057
                },
                "thickness": 10
            },
                {
                    "position": {
                        "x": 7.04712,
                        "y": -284.96567
                    },
                    "thickness": 14
                },
                {
                    "position": {
                        "x": -42.85882,
                        "y": -603.58046
                    },
                    "thickness": 20
                }]

            for (let p = 0; p < strs.length; p++)
            {
                var path = parsePathNode(strs[p])
                var group = new THREE.Group();
                group.position.set(roaddata[p].position.x, 0, roaddata[p].position.y)

                for (var j = 0, jl = path.subPaths.length; j < jl; j++)
                {

                    var subPath = path.subPaths[j];

                    var points = subPath.getPoints();
                    var v3ps = [];
                    for (let k = 0; k < points.length; k++)
                    {

                        const p2o = points[k];
                        v3ps.push(new THREE.Vector3(p2o.x, 0, p2o.y))
                    }
                   ;

                    var geometry = cga.extrudeToGeometryBuffer(
                        [
                            cga.v3( 0, 0,-roaddata[p].thickness / 2,),
                            cga.v3(0, 0,roaddata[p].thickness / 2,)]
                        , v3ps, {
                        normal: cga.v3(0, 0, 1),
                        isClosed: true,
                        sealStart: false,
                        sealEnd: false,
                        textureScale: new cga.Vector2(1 / roaddata[p].thickness, 1 / 2500)
                    })
                   // let geometry=

                    var roadMesh = new THREE.Mesh(geometry, roadmats[p]);

                    group.add(roadMesh);
                    group.position.set(-104.29041, 0, -303.55284);
                }
                roadGroup.add(group);
                roadGroup.rotation.y = Math.PI / 2;
            }
            scene.add(roadGroup)
            function initControls() {
                const  controls = new THREE.OrbitControls(camera, renderer.domElement);
                // 如果使用animate方法时,将此函数删除
                //controls.addEventListener( 'change', render );
                // 使动画循环使用时阻尼或自转 意思是否有惯性
                controls.enableDamping = true;
                //动态阻尼系数 就是鼠标拖拽旋转灵敏度
                //controls.dampingFactor = 0.25;
                //是否可以缩放
                controls.enableZoom = true;
                //是否自动旋转
                controls.autoRotate = true;
                controls.autoRotateSpeed = 0.5;
                //设置相机距离原点的最远距离
                controls.minDistance = 1;
                //设置相机距离原点的最远距离
                //controls.maxDistance = 200;
                //是否开启右键拖拽
                controls.enablePan = true
                return controls
            }
            const  controls = initControls()

            function animate() {
                roadmats[0].map.offset.x += 0.003
                roadmats[1].map.offset.x += 0.006
                roadmats[2].map.offset.x += 0.009
                controls.update()
                renderer.render(scene,camera)
                requestAnimationFrame(animate)
            }
            animate()


        }});





    function animate() {
        controls.update()


        renderer.render(scene, camera)
        requestAnimationFrame(animate)
    }

    animate()


</script>

</body>
</html>

 

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Three.js是一个用于创建和展示3D图形的JavaScript库。它提供了丰富的功能和工具,使开发者能够在Web浏览器中创建出色的3D场景和动画效果。 关于Three.js中的道路流光效果,通常可以通过以下步骤实现: 1. 创建场景(Scene):使用Three.js创建一个3D场景,作为展示道路流光效果的容器。 2. 创建相机(Camera):选择适合你场景需求的相机类型,例如透视相机(PerspectiveCamera)或正交相机(OrthographicCamera)。 3. 创建道路模型:使用Three.js的几何体(Geometry)和材质(Material)来创建道路模型。可以使用平面几何体(PlaneGeometry)或自定义几何体来创建道路形状,并为其应用合适的材质。 4. 创建流光效果:通过使用着色器(Shader)来实现流光效果。着色器是一种在GPU上运行的程序,可以自定义渲染管线的行为。你可以编写自己的着色器代码,或者使用现有的着色器库,如Three.js提供的ShaderMaterial。 5. 添加道路模型到场景:将道路模型添加到场景中,以便在渲染时显示出来。 6. 渲染场景:使用Three.js提供的渲染器(Renderer)将场景和相机渲染到屏幕上。你可以选择使用WebGLRenderer或者CanvasRenderer,具体取决于你的需求和浏览器支持情况。 这只是一个简单的概述,实现道路流光效果可能涉及更多的细节和技术。你可以参考Three.js的官方文档和示例来获取更详细的信息和代码示例。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值