HTML5 WebGL로 구현한 3D 컴퓨터실 예시

小云云
풀어 주다: 2018-03-19 10:06:31
원래의
4479명이 탐색했습니다.

WebGL로 렌더링된 3D 컴퓨터실은 이제 새로운 것이 아닙니다. 이번 글의 주요 목적은 프로젝트에서 우연히 사용되었던 3D 컴퓨터실의 눈과 중심의 문제를 설명하는 것입니다. 결국 이 예시가 제 요구사항에 가장 잘 맞는 것 같아서 기록으로 활용했습니다.

Rendering

이 3D 컴퓨터실 데모도 꽤 좋고, 예쁘고, 기본적인 상호작용도 만족스럽습니다. 구현 방법을 살펴보겠습니다.

코드 생성

정의 클래스

먼저 index.html에서 호출된 js 경로에서 해당 js를 하나씩 엽니다. Editor.Server 클래스는 server.js에서 사용자 정의되고 HT로 캡슐화됩니다. Default.def 함수로(생성된 클래스 이름 Editor.Server 앞에 있는 Editor는 E로 대체할 수 없습니다.):


ht.Default.def('Editor.Server', Object, {//第一个参数为类名,如果为字符串,自动注册到HT的classMap中;第二个参数为此类要继承的父类;第三个参数为方法和变量的声明
    addToDataModel: function(dm) { //将节点添加进数据容器
        dm.add(this._node);// ht 中的预定义函数,将节点通过 add 方法添加进数据容器中
    },
    setHost: function() { //设置吸附
        this._node.setHost.apply(this._node, arguments); 
    },
    s3: function() {//设置节点的大小
        this._node.s3.apply(this._node, arguments);
    },
    setElevation: function() {//控制Node图元中心位置所在3D坐标系的y轴位置
        this._node.setElevation.apply(this._node, arguments);
    }
});
로그인 후 복사

Editor.Server 클래스를 생성합니다.

이 클래스는 ht.Node 노드를 만들고 노드의 색상과 전면 텍스처를 설정합니다.


var S = E.Server = function(obj) {//服务器组件
    var color = obj.color, 
        frontImg = obj.frontImg;

    var node = this._node = new ht.Node();//创建节点
    node.s({//设置节点的样式 s 为 setStyle 的缩写
        'all.color': color,//设置节点六面的颜色
        'front.image': frontImg //设置节点正面的图片
    });
};
로그인 후 복사

이런 식으로 서버 구성 요소를 생성해야 하는 곳에 새 서버 구성 요소 개체를 직접 생성할 수 있고, 위에서 선언한 setHost 및 기타 함수는 곧 필요할 것입니다.

다음으로 Editor.Cabinet 캐비닛 클래스를 만듭니다. 이 방법은 위의 Editor.Server 클래스 정의 방법과 유사합니다.


ht.Default.def('Editor.Cabinet', Object, {
    addToDataModel: function(dm) {
        dm.add(this._door);
        dm.add(this._node);
        this._serverList.forEach(function(s) { 
            s.addToDataModel(dm); 
        });
    },
    p3: function() { 
        this._node.p3.apply(this._node, arguments);//设置节点的 3d 坐标
    }
});
로그인 후 복사

Editor.Cabinet 클래스 만들기

이 클래스는 이전 Editor.Server 서버 구성 요소 클래스는 좀 더 복잡합니다. 이 클래스는 캐비닛 내부에 캐비닛 본체, 캐비닛 도어 및 서버 구성 요소를 생성합니다.


var C = E.Cabinet = function(obj) {
    var color = obj.color,
        doorFrontImg = obj.doorFrontImg,
        doorBackImg = obj.doorBackImg,
        s3 = obj.s3;

    var node = this._node = new ht.Node(); // 柜身
    node.s3(s3);//设置节点的大小 为 setSize3d
    node.a('cabinet', this);//自定义 cabinet 属性
    node.s({//设置节点的样式 为 setStyle
        'all.color': color,//设置节点六面的颜色
        'front.visible': false//设置节点前面是否可见
    });

    if (Math.random() > 0.5) {
        node.addStyleIcon('alarm', {//向节点上添加 icon 图标
            names: ['icon 温度计'],//包含多个字符串的数组,每个字符串对应一张图片或矢量(通过ht.Default.setImage注册)
            face: 'top',//默认值为front,图标在3D下的朝向,可取值left|right|top|bottom|front|back|center
            position: 17,//指定icons的位置
            autorotate: 'y',//默认值为false,图标在3D下是否自动朝向眼睛的方向
            t3: [0, 16, 0],//默认值为undefined,图标在3D下的偏移,格式为[x,y,z]
            width: 37,//指定每个icon的宽度,默认根据注册图片时的宽度
            height: 32,//指定每个icon的高度,默认根据注册图片时的高度
            textureScale: 4,//默认值为2,该值代表内存实际生成贴图的倍数,不宜设置过大否则影响性能
            visible: { func: function() { return !!E.alarmVisible; }}//表示该组图片是否显示
        });
    }

    var door = this._door = new ht.DoorWindow();//柜门
    door.setWidth(s3[0]);//置图元在3D拓扑中的x轴方向的长度
    door.setHeight(1);//设置图元在3D拓扑中的z轴长度
    door.setTall(s3[1]);//控制Node图元在y轴的长度
    door.setElevation(0);//设置图元中心在3D坐标系中的y坐标
    door.setY(s3[2] * 0.5);//设置节点在 y 轴的位置
    door.setHost(node);//设置吸附
    door.s({//设置节点样式 setStyle
        'all.color': color,//设置节点六面颜色
        'front.image': doorFrontImg,//设置节点正面图片
        'front.transparent': true,//设置节点正面是否透明
        'back.image': doorBackImg,//设置节点背面的图片
        'back.uv': [1,0, 1,1, 0,1, 0,0],//自定义节点后面uv贴图,为空采用默认值[0,0, 0,1, 1,1, 1,0]
        'dw.axis': 'right'//设置DoorWindow图元展开和关闭操作的旋转轴,可取值left|right|top|bottom|v|h
    });

    var serverList = this._serverList = []; 
    var max = 6,
        list = E.randomList(max, Math.floor(Math.random() * (max - 2)) + 2); //global.js 中声明的获取随机数的函数 
    var server, h = s3[0] / 4;
    list.forEach(function(r) {
        var server = new E.Server({ //服务器组件
            color: 'rgb(51,49,49)',
            frontImg: '服务器 组件精细'
        });
        server.s3(s3[0] - 2, h, s3[2] - 4);//设置节点大小
        server.setElevation((r - max * 0.5) * (h + 2));//设置节点中心点在 y 轴的坐标
        server.setHost(node);//设置节点的吸附

        serverList.push(server);//向 serverList 中添加 server 节点
    });
};
로그인 후 복사

위 코드에서 언급되지 않은 유일한 것은 Editor입니다. global.js 파일에 있는 .randomList 함수의 선언은 다음과 같습니다.


var E = window.Editor = {
    leftWidth: 0,
    topHeight: 40,
    randomList: function(max, size) {
        var list = [], ran;
        while (list.length < size) {
            ran = Math.floor(Math.random() * max);
            if (list.indexOf(ran) >= 0) 
                continue;
            list.push(ran);
        }
        return list;
    }
};
로그인 후 복사

자, 장면의 각 부분에 대한 클래스가 생성되었으므로 장면을 생성한 다음 이러한 프리미티브를 파일에 쌓아야 합니다. 그것!

Scene Creation

이에 익숙한 학생은 HT를 사용하여 3D 장면을 만들려면 새로운 3D 구성 요소만 필요하고 addToDOM 함수를 통해 장면을 본문에 추가해야 한다는 점을 알아야 합니다.


var g3d = E.main = new ht.graph3d.Graph3dView(); //3d 场景
로그인 후 복사

main .js 파일의 주요 목적은 벽, 바닥, 문, 에어컨, 모든 캐비닛의 생성 및 배출 위치 등 3D 장면에 필요한 몇 가지 요소와 매우 중요한 대화형 부분을 만드는 것입니다.

벽, 바닥, 문, 에어컨, 캐비닛 등의 생성 코드는 올리지 않겠습니다. 관심 있으신 분들은 직접 코드를 확인해 보시기 바랍니다. 여기서는 주로 캐비닛 더블클릭과 관련 개체에 대해 이야기하겠습니다. 캐비닛(캐비닛 도어, 서버 장비) 그러면 3D에서 카메라의 시선이 더블클릭된 캐비닛 앞의 특정 위치로 이동하게 되는데, 이 움직임이 매우 부드럽습니다. 이전 기술이 좋지 않았기 때문에 이런 생각을 했습니다. 부분을 ​​오랫동안 설명하고 마지막으로 이 데모의 구현 방법을 참조했습니다.

눈과 중심을 반복적으로 설정할 수 있도록 이 두 매개변수 설정에 해당하는 콘텐츠는 setEye 및 setCenter 메서드로 캡슐화됩니다. setCenter 메서드는 setEye 메서드와 유사하며 여기서는 반복하지 않습니다.


// 设置眼睛位置
var setEye = function(eye, finish) {
    if (!eye) return;
    var e = g3d.getEye().slice(0),//获取当前 eye 的值
        dx = eye[0] - e[0],
        dy = eye[1] - e[1],
        dz = eye[2] - e[2];
    // 启动 500 毫秒的动画过度
    ht.Default.startAnim({
        duration: 500,
        easing: easing,//动画缓动函数
        finishFunc: finish || function() {}, //动画结束后调用的函数
        action: function(v, t) {//设置动画v代表通过easing(t)函数运算后的值,t代表当前动画进行的进度[0~1],一般属性变化根据v参数进行
            g3d.setEye([ //设置 3D 场景中的 eye 眼睛的值,为一个数组,分别对应 x,y,z 轴的值 
                e[0] + dx * v,
                e[1] + dy * v,
                e[2] + dz * v
            ]);
        }
    });
};
로그인 후 복사

setCenter를 반복적으로 선언하지 않았습니다. 함수가 이 함수가 중요하지 않다는 의미는 아니지만, 반대로 "시력"을 이동하는 과정에서 이 함수가 결정적인 역할을 합니다. 위의 setEye 함수는 제가 가고 싶은 것과 동일합니다. 내 목표 위치 앞에 (적어도 정의했을 때는) 이 용도), sCenter의 정의는 내 시야를 목표 위치로 이동시키는 것입니다(예를 들어 현재 위치에 서서 내 뒤에 있는 물체를 볼 수 있습니다) 또는 오른쪽 뒤쪽으로 가서 물체 위에 설 수 있습니다. 앞서 읽어보세요.) 이것은 매우 중요합니다. 꼭 맛보시기 바랍니다.

두 번 클릭 이벤트는 간단합니다. HT 캡슐화된 이벤트를 듣고, 이벤트 유형을 결정하고, 해당 조치를 취하세요.


g3d.mi(function(e) {//addInteractorListener 事件监听函数
    if (e.kind !== &#39;doubleClickData&#39;)  //判断事件类型为双击节点
        return;
    var data = e.data, p3;

    if (data.a(&#39;cabinet&#39;)) //机身
        p3 = data.p3();
    else {
        host = data.getHost(); //获取点击节点的吸附对象
        if (host && host.a(&#39;cabinet&#39;)) {//如果吸附对象为 cabinet
            p3 = host.p3();
        }
    }

    if (!p3) return;

    setCenter(p3); //设置 center 目标的要移向位置为 cabinet 的位置
    setEye([p3[0], 211, p3[2] + 247]); //设置 eye 眼睛要移向的位置
});
로그인 후 복사

상단 탐색 표시줄

처음 이 예를 봤을 때. 그 때 이 사람 정말 대단하다고 생각했어요. 제가 HT를 오랫동안 사용해왔지만 아직도 HT의 ht.widget.Toolbar를 사용해서 이렇게 아름다운 효과를 만들어내지는 못했어요. 실제로 양식을 사용하여 수행되었다는 것을 깨달았습니다. 정말 바보입니다.


var form = E.top = new ht.widget.FormPane(); //顶部 表单组件
form.setRowHeight(E.topHeight);//设置行高
form.setVGap(-E.topHeight);//设置表单组件水平间距 设置为行高的负值则可以使多行处于同一行
form.setVPadding(0);//设置表单顶部和顶部与组件内容的间距
form.addRow([null, {//向表单中添加一行组件,第一个参数为元素数组,元素可为字符串、json格式描述的组件参数信息、html元素或者为null
    image: {
        icon: &#39;./symbols/inputBG.json&#39;,
        stretch: &#39;centerUniform&#39;
    }
}], [40, 260]);//第二个参数为每个元素宽度信息数组,宽度值大于1代表固定绝对值,小于等于1代表相对值,也可为80+0.3的组合
form.addRow([null, null, {
    id: &#39;searchInput&#39;,
    textField: {}
}, {
    element: &#39;机房可视化管理系统&#39;,
    color: &#39;white&#39;,
    font: &#39;18px arial, sans-serif&#39;
}, null, {
    button: {
        // label: &#39;视图切换&#39;,
        icon: &#39;./symbols/viewChange.json&#39;,
        background: null,
        selectBackground: &#39;rgb(128,128,128)&#39;,
        borderColor: &#39;rgba(0, 0, 0, 0)&#39;,
        onClicked: function() {
            E.focusTo();
        }
    }
}, null, {
    button: {
        // label: &#39;告警&#39;,
        icon: &#39;./symbols/alarm.json&#39;,
        togglable: true,
        selected: false,
        background: null,
        selectBackground: &#39;rgb(128,128,128)&#39;,
        borderColor: &#39;rgba(0, 0, 0, 0)&#39;,
        onClicked: function(e) {
            E.setAlarmVisible(this.isSelected());
        }
    }
}, null], [40, 42, 218, 300, 0.1, 50, 10, 50, 10]);
로그인 후 복사

위 내용은 구현만 가능하지만 실제로 html 태그에 추가되지 않아 현재 인터페이스에는 아무것도 없다는 의미입니다! 페이지가 로드될 때 본문에 3D 장면을 추가하는 것을 잊지 말고, 창 크기 변경 이벤트를 설정할 때 양식도 실시간으로 업데이트되어야 합니다.


window.addEventListener(&#39;load&#39;, function() {
    g3d.addToDOM(); //将 3D 场景添加进 body 中

    document.body.appendChild(E.top.getView()); //将 form 表单组件底层 p 添加进 body 中

    window.addEventListener(&#39;resize&#39;, function() {//窗口大小变化事件监听
        E.top.iv();//更新 form 表单的底层 p 
    });
});
로그인 후 복사

这里说明一下 addToDOM 函数,对于了解 HT 的机制非常重要。HT 的组件一般都会嵌入 BorderPane、SplitView 和 TabView 等容器中使用,而最外层的 HT 组件则需要用户手工将 getView() 返回的底层 p 元素添加到页面的 DOM 元素中,这里需要注意的是,当父容器大小变化时,如果父容器是 BorderPane 和 SplitView 等这些 HT 预定义的容器组件,则 HT 的容器会自动递归调用孩子组件invalidate 函数通知更新。但如果父容器是原生的 html 元素, 则 HT 组件无法获知需要更新,因此最外层的 HT 组件一般需要监听 window 的窗口大小变化事件,调用最外层组件 invalidate 函数进行更新。

为了最外层组件加载填充满窗口的方便性,HT 的所有组件都有 addToDOM 函数,其实现逻辑如下,其中 iv 是 invalidate 的简写:


addToDOM = function(){   
    var self = this,
        view = self.getView(),   
        style = view.style;
    document.body.appendChild(view); //将场景的底层 p 添加进 body 中           
    style.left = &#39;0&#39;;//HT 默认将所有的组件底层p的position设置为absolute
    style.right = &#39;0&#39;;
    style.top = &#39;0&#39;;
    style.bottom = &#39;0&#39;;      
    window.addEventListener(&#39;resize&#39;, function () { self.iv(); }, false); //窗口大小变化监听事件,通知组件变化更新          
}
로그인 후 복사

这样,所有的代码就结束了,可以自己右键“检查”,network 中可以获取相对应的 json 文件。

相关推荐:

打造最美HTML5 3D机房(MONO哥强势归来,第四季惊艳发布)_html/css_WEB-ITnose

위 내용은 HTML5 WebGL로 구현한 3D 컴퓨터실 예시의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!