基于html5canvas的3d模型贴图问题

前端学习123321123      2022-02-13     389

关键词:

之前注意到的一个例子,但是一直没有沉下心来看这个例子到底有什么优点,总觉得就是一个 list 列表,也不知道右边的 3d 场景放两个节点是要干嘛,今天突然想起来就仔细地看了一下这个例子的代码,实际操作中应该还是有用处的,就跟大家分享一下。

本例地址: http://hightopo.com/guide/gui...

实现图如下,看起来略有点简陋,但是可以自己天马心空增加或者更改成你需要的东西:

技术图片

首先,创建场景,HT 中有一个 BorderPane 面板组件是拿来页面排布的,可以排布 html 标签,也可以排布 HT 的组件,这里我们将整个页面分为三个部分,顶部工具条 toolbar、左侧列表 listView 和中间 3d 场景 g3d,再将这个面板组件添加进 html body 体中:

borderPane = new ht.widget.BorderPane();//面板组件                
toolbar = new ht.widget.Toolbar(); //工具条                    
                                
listView = new ht.widget.ListView(); //列表组件
g3d = new ht.graph3d.Graph3dView();// 3d 组件
                
borderPane.setTopView(toolbar);//将 toolbar 放置到面板中的顶部
borderPane.setLeftView(listView, 350); //将 listView 放置到面板中的左侧
borderPane.setCenterView(g3d); //将 g3d 放置到面板中的中间
                
borderPane.addToDOM(); //将面板组件添加进 body 中

addToDOM 函数是 HT 封装好的将 HT 组件添加进 body 体中的一个方法,其实现逻辑如下:

addToDOM = function(){   
    var self = this,
          view = self.getView(),//通过 getView 函数获取组件的底层 div
          style = view.style;
    document.body.appendChild(view); //body 添加孩子 view           
    style.left = ‘0‘;
    style.right = ‘0‘;
    style.top = ‘0‘;
    style.bottom = ‘0‘;      
    window.addEventListener(‘resize‘, function () { self.iv(); }, false);//窗口大小变化时,立即刷新组件            
}

我们一个部分一个部分来解析,从最上层的 toolbar 工具条开始,如下:

技术图片

工具条也是分为三个部分,一是左侧的搜索框,二是中间的分割线,三是右侧的点击按钮。

我们首先向工具条 toolbar 中添加这三个元素,具体添加方法请参考 HT for Web 工具条手册:

toolbar.setItems([//设置工具条元素数组              
    {
        id: ‘text‘,
        label: ‘Search‘,
        icon: ‘images/search.png‘,
        textField: {
            width: 120
        }
    },
    ‘separator‘,
    {
        label: ‘Sort by price‘,
        type: ‘toggle‘,//toggle表示开关按钮
        selected: true,
        action: function(){
            listView.setSortFunc(this.selected ? sortFunc : null);
        }
    }
]);

接下来向左侧的 listView 列表中添加数据,这个数据就是 product.js 中的变量 products,通过遍历这个数组变量,将这个数组中的所有值都填充到 listView 列表中:

技术图片

products.forEach(function(product){//products 是在product.js 文件中定义的
    var data = new ht.Data();
    data.a(product);//设置数据 data 的 attr 属性
    、listView.dm().add(data);//将 data 添加进 listView 的数据容器中
}); 

然后对 listView 列表进行一系列的样式属性的设置:行高、背景、icon 图标、文字提示等等。代码如下,解释都在代码中了,还有不懂的请查阅 HT for Web 列表手册

listView.setRowHeight(50);//设置行高
listView.drawRowBackground = function(g, data, selected, x, y, width, height){//绘制行背景色,默认仅在选中该行时填充选中背景色,可重载自定义
    if(this.isSelected(data)){//选中时
        g.fillStyle = ‘#87A6CB‘;
    }
    else if(this.getRowIndex(data) % 2 === 0){//偶数行时
        g.fillStyle = ‘#F1F4F7‘;
    }
    else{
        g.fillStyle = ‘#FAFAFA‘;
    }
    g.beginPath();
    g.rect(x, y, width, height);
    g.fill();
};
// HT 通过 ht.Default.setImage(‘name‘, json) 函数来注册图片
ht.Default.setImage(‘productIcon‘, {
    width: 50,
    height: 50,
    clip: function(g, width, height) {//clip 用于裁剪绘制区域
        //利用canvas画笔绘制,实现自定义裁剪任意形状的效果
        //这里是将图片裁剪成圆形
        g.beginPath();
        //x, y, radius, startAngle, endAngle, anticlockwise
        g.arc(width/2, height/2, Math.min(width, height)/2-3, 0, Math.PI * 2, true);
        g.clip();
    },
    comps: [//矢量图形的组件Array数组,每个数组对象为一个独立的组件类型,数组的顺序为组件绘制先后顺序
        {
            type: ‘image‘,
            stretch: ‘uniform‘,//图片始终保持原始宽高比例不变化,并尽量填充满矩形区域
            rect: [0, 0, 50, 50],//指定组件绘制在矢量中的矩形边界 [x, y, width, height]四个参数方式,分别代表左上角坐标x和y,以及宽高width和height
            name: {func: function(data){return data.a(‘ProductId‘);}}//图片的名字为 data.a(‘ProductId‘) 返回的值
        }
    ]
});
listView.setIndent(60);//设置indent缩进,该参数一般用于指定图标的宽度                 
listView.getIcon = function(data){//返回data对象对应的icon图标,可重载自定义
    return ‘productIcon‘;//这个是前面 ht.Default.setImage 函数注册过的矢量图形
};  
                
listView.enableToolTip();//开启文字提示
listView.getLabel = function(data){//返回data对象显示的文字,默认返回data.toLabel(),可重载自定义
    return data.a(‘ProductName‘) + ‘ - $‘ + data.a(‘UnitPrice‘).toFixed(2);
};                
listView.getToolTip = function(e){//根据传入的交互事件,返回文本提示信息,可重载自定义
    var data = this.getDataAt(e);//传入逻辑坐标点或者交互event事件参数,返回当前点下的数据元素
    if(data){
        return ‘<span style="color:#3D97D0">ProductId:&nbsp;</span>‘ + data.a(‘ProductId‘) + ‘<br>‘ +  
                       ‘<span style="color:#3D97D0">ProductName:&nbsp;</span>‘ + data.a(‘ProductName‘) + ‘<br>‘ +  
                       ‘<span style="color:#3D97D0">QuantityPerUnit:&nbsp;</span>‘ + data.a(‘QuantityPerUnit‘) + ‘<br>‘ + 
                       ‘<span style="color:#3D97D0">Description:&nbsp;</span>‘ + data.a(‘Description‘);                        
    }
    return null;
};

列表组件中还封装了一个很方便的函数 setSortFunc,用于设置排序函数,用户也可以自定义,目前我们希望对这些”商品“进行排序:

sortFunc = function(d1, d2){//自定义排序函数
    return d1.a(‘UnitPrice‘) - d2.a(‘UnitPrice‘);
};
listView.setSortFunc(sortFunc);//HT 定义的 设置排序函数

因为我们要进行数据的搜索,就要对数据以及显示方面进行过滤,因为在数据变化时,HT 无法获知需要更新,这时候就要我们手动对有显示变化的部分调用更新函数 invalidate 简写为 iv。

我们对文本输入框的键盘弹起事件进行事件的监听,然后判断我们输入的值在 listView 列表中是否存在等操作对显示界面进行过滤:

// 对text文本框进行键盘按键弹起事件监听 toolbar.getItemById(‘text‘).element.getElement().onkeyup = function(e){
    listView.invalidateModel();//无效模型,最彻底的刷新方式 “完全刷新”
};
//如果文本框输入的值在
listView.setVisibleFunc(function(data){//设置可见过滤器                    
    var text = toolbar.v(‘text‘);//getValue(id)根据id获取对应item元素值,简写函数为v(id)
    if(text){              
        return data.a(‘ProductName‘).toLowerCase().indexOf(text.toLowerCase()) >= 0;//indexOf()方法返回在类型数组中可以找到给定元素的第一个索引,如果不存在,则返回-1
    }
    return true;
});

第三个部分,右侧 3d 场景,利用的是 HT 的三维组件 ht.graph3d.Graph3dView,然后在 3d 场景上添加两个节点,作为对照:

//创建两个节点放到 3d 场景中
var node = new ht.Node();
node.s3(30, 30, 30);//设置三维大小
node.p3(-30, 15, 0);//设置三维坐标
node.s(‘all.color‘, ‘#87A6CB‘);//设置 node 的六个面颜色                
g3d.dm().add(node);//将新建的 node 添加进 3d 场景的数据容器中
                
var node = new ht.Node();
node.s3(30, 30, 30);
node.p3(30, 15, 0);
node.s(‘all.color‘, ‘#87A6CB‘);
node.setElevation(15);
g3d.dm().add(node);
                
g3d.setEye(-100, 100, 80);//设置 3d 场景的眼睛(或Camera)所在位置,默认值为[0, 300, 1000]
g3d.setGridVisible(true);//设置是否显示网格
g3d.setGridColor(‘#F1F4F7‘);//设置网格线颜色

整个场景创建完毕,接下来就是将 listView 中显示的 icon 图标拖拽到 3d 中的节点上,作为贴图。列表组件中封装了一个拖拽的功能 handleDragAndDrop,这个函数有两个参数,event 交互事件和 state 当前状态,我们对拖拽事件的不同状态进行不同的处理:

listView.handleDragAndDrop = function(e, state){//该函数默认为空,若该函数被重载,则pan平移组件功能将被关闭
    if(state === ‘prepare‘){//state当前状态,先后会有prepare-begin-between-end四种过程
        var data = listView.getDataAt(e);//传入逻辑坐标点或者交互event事件参数,返回当前点下的数据元素
        listView.sm().ss(data);//设置选中当前事件所在的数据元素
        if(dragImage && dragImage.parentNode){
            document.body.removeChild(dragImage);
        }
        dragImage = ht.Default.toCanvas(‘productIcon‘, 30, 30, ‘uniform‘, data);
        // toCanvas(image, width, height, stretch, data, view, color)将图片转换成Canvas对象
        productId = data.a(‘ProductId‘);
    }
    else if(state === ‘begin‘){
        if(dragImage){
            var pagePoint = ht.Default.getPagePoint(e);//返回page属性坐标
            dragImage.style.left = pagePoint.x - dragImage.width/2 + ‘px‘;//实时更新拖拽时的图标的位置
            dragImage.style.top = pagePoint.y - dragImage.height/2 + ‘px‘;
            document.body.appendChild(dragImage);//在 html body 体中添加这个拖拽的图片
        }
    }
    else if(state === ‘between‘){
        if(dragImage){
            var pagePoint = ht.Default.getPagePoint(e);//返回page属性坐标
            dragImage.style.left = pagePoint.x - dragImage.width/2 + ‘px‘;
            dragImage.style.top = pagePoint.y - dragImage.height/2 + ‘px‘;   
                            
            if(ht.Default.containedInView(e, g3d)){//判断交互事件所处位置是否在View组件之上,一般用于Drog And Drop的拖拽操作判断
            //这边做了两个判断,一个是鼠标在拖拽的时候未松开,一个是鼠标拖拽的时候松开了。
                if(lastFaceInfo){//鼠标未松开的情况下,贴图显示旧值
                //data.face 默认值为front,图标在3D下的朝向,可取值left|right|top|bottom|front|back|center
                    lastFaceInfo.data.s(lastFaceInfo.face + ‘.image‘, lastFaceInfo.oldValue);
                    lastFaceInfo = null;
                }
                //鼠标松开时,将新值赋给这个面
                var faceInfo = g3d.getHitFaceInfo(e);//获取鼠标所在面信息
                if(faceInfo){
                    faceInfo.oldValue = faceInfo.data.s(faceInfo.face + ‘.image‘);//获取面的“老值”
                    faceInfo.data.s(faceInfo.face + ‘.image‘, productId);//front/back/top/bottom/left/right.image 设置这些面的贴图
                    lastFaceInfo = faceInfo;
                }
            }
        }
    }
    else{//拖拽结束之后,所有值都回到初始值
        if(dragImage){//有从列表中拖拽图片
            if(lastFaceInfo){//有赋“图片”到 3d 中的节点上
                lastFaceInfo.data.s(lastFaceInfo.face + ‘.image‘, lastFaceInfo.oldValue);
               lastFaceInfo = null;
           }                             
           if(ht.Default.containedInView(e, g3d)){                               
               var faceInfo = g3d.getHitFaceInfo(e);
               if(faceInfo){
                   faceInfo.data.s(faceInfo.face + ‘.image‘, productId);
               }
           }                                                        
           if(dragImage.parentNode){
               document.body.removeChild(dragImage);
           }
           dragImage = null;  
           productId = null;
       }
   }
}; 

unity3d怎么给模型贴图

...不能出现中文,否则Unity3d无法识别。参考技术A您好max的模型导入Unity3d里面,导入的时候只是导入模型而已。至于把贴图也导入的话,您可以试试把模型导出为FBX的格式,然后再把贴图文件和FBX的模型文件放到同一个文件夹中,... 查看详情

基于html5canvas的3d热力云图效果

前言  数据蕴藏价值,但数据的价值需要用 IT 技术去发现、探索,可视化可以帮助人更好的去分析数据,信息的质量很大程度上依赖于其呈现方式。在数据分析上,热力图无疑是一种很好的方式。在很多行业中都有着... 查看详情

unity3d使用fbx格式的外部模型②(fbx模型与默认3d模型的区别|fbx模型贴图查找路径|fbx模型可设置多个材质)

文章目录一、FBX模型与默认3D模型的区别二、FBX模型贴图查找路径三、FBX模型可设置多个材质在FBX文件中包含了网格,材质,纹理贴图信息;网格Mesh:表示3D物体的形状;材质Material:表示3D物体的表面特性;纹理贴图Texture:定义3D物体表面... 查看详情

如何在 Blender 中从不同的摄像机角度渲染 3d 模型的不同贴图(漫反射、深度、镜面反射、阴影)?

】如何在Blender中从不同的摄像机角度渲染3d模型的不同贴图(漫反射、深度、镜面反射、阴影)?【英文标题】:Howtorenderdifferentmaps(diffuse,depth,specular,shaded)ofa3dmodelfromdifferentcameraangles,inBlender?【发布时间】:2021-11-2011:17:33【问题... 查看详情

“obj”是啥意思?

...整贴图坐标。参考技术AOBJ是Alias|Wavefront公司为它的一套基于工作站的3D建模和动画软件"AdvancedVisualizer"开发的一种标准3D模型文件格式,很适合用于3D软件模型之间的互导,也可以通过Maya读写。比如在3dsMax或LightWave中建了一... 查看详情

unity3d里面想利用贴图实现模型表面有凹凸质感具体如何操作?

3dmax做的模型,在Unity里面想利用贴图实现模型表面有凹凸质感具体如何操作?菜鸟,求高手详解就比如做一个房子的房顶那么多瓦片不想精模,想利用贴图来使得屋顶有瓦片的凹凸质感,unity里如何实现?用法线贴图如果没有法... 查看详情

androidopengles2.0(十六)——3d模型贴图及光照处理(obj+mtl)(代码片段)

在AndroidOpenGLES2.0(十四)——Obj格式3D模型加载中实现了Obj格式的3D模型的加载,加载的是一个没有贴图,没有光照处理的帽子,为了呈现出立体效果,“手动”加了光照,拥有贴图的纹理及光照又该怎... 查看详情

计算机图形学----基于3d图形开发技术

第1章游戏模型第2章顶点处理机制第3章光栅化操作第4章片元处理和输出合并第5章光照和着色第6章参数曲线和表面第7章着色器模型第8章图像纹理第9章凹凸贴图第10章高级纹理操作第11章角色动画第12章物理模拟参考文献 第1... 查看详情

3d 法线贴图在 Three.js 中无法正常工作

...我想在我的网格中添加一个法线贴图,当我应用到它时,模型变得平坦而不平滑,我没有得到我想要的效果。这是我的材料代码:varmaterial=newTHREE.MeshPhongMaterial(map:THREE.ImageUtils.l 查看详情

基于全景照片和场景模型计算导出纹理贴图

基于全景照片和场景模型计算导出纹理贴图  首先,假设全景的位姿是已知,一般可以通过传感器测量或者相关标定方法计算得到;另外,还假设相机中心与模型坐标中心重合(只有这样才能将全景和模型映射到标准球上);... 查看详情

fbx格式的模型是多边形的么

...骼动画,先了解FBX格式,并且给了示例代码结合着理解,基于FBXSDK的FBX模型解析与加载先对FBXSDK有个认识,然后可以看下fbxsdkfileinport/export(译),对FBXSDK的命名规则了解下,代码中几乎按照这个写的,同时这篇对步骤进行了细分... 查看详情

基于html5canvas实现矢量工控风机叶轮旋转

之前在拓扑上的应用都是些静态的图元,今天我们将在拓扑上设计一个会动的图元——叶轮旋转。先看看最后我们实现的效果:http://www.hightopo.com/demo/...我们先来看下这个叶轮模型长什么样从模型上看,这个叶轮模型有三个叶片... 查看详情

unity支持的模型格式及详细介绍

....gtlf4.obj简述: .obj文件是Alias|Wavefront公司为它的一套基于工作站的3D建模和动画软件"AdvancedVisualizer"开发的一种标准3D模型文件格式,很适合用于3D软件模型之间的互导,也可以通过Maya读写。 .obj文件是一种文本文件&... 查看详情

obj是啥文件?obj文件怎么打开

obj文件是3D模型文件格式。由Alias|Wavefront公司为3D建模和动画软件"AdvancedVisualizer"开发的一种标准,适合用于3D软件模型之间的互导,也可以通过Maya读写。比如你在3dsMax或LightWave中建了一个模型,想把它调到Maya里面渲染或... 查看详情

(四)unity纹理、贴图和材质

...创建一个Terrain(地形)和一个Cube(立方体),然后我们基于这两个物体来深入学习什么是纹理、贴图和材质。下面我们添加一个地形纹理和一个LOL泳池派对纹理。(其实就是图片)我们来学习一下纹理的Inspector视图中都有什么... 查看详情

3d场景的制作步骤

...的游戏场景的范围,视角是随着玩家的移动不停变换的,基于这点才有了场景分块的必要性。场景分块:在游戏中不管你的视角在哪个位置,可能你的视角方向上只能看到几百个面的模型。如果将整个场景一起导出,游戏中会把... 查看详情

sdf(signeddistancefield)

...用来表示字体,原神的面部渲染中阴影部分贴图也是基于sdf生成的。        SDF的本质就是存储每个点到图形的最近距离,即将模型划出一个表 查看详情

3dmax导出fbx格式原模型是dds的贴图

用3Dmax导出FBX格式的模型可是原模型是dds的贴图导出FBX后在导入别的软件模型就是黑的材质球上也没材质求大神求大神。。。。。一般网上下载的模型多数会出现你这种情况,就是打开一片漆黑,这个就是材质混乱的问题或者材... 查看详情