文档章节

three.js 地形法向量生成

李勇2
 李勇2
发布于 2015/03/02 09:38
字数 1264
阅读 47
收藏 0

上一节采用 分形算法生成地形的高度值, 接着我们需要生成每个顶点的法向量。


three.js 的PlaneGeometry 自带有法向量, 法向量分为两种 即 平面法向量 和 平面每个定点法向量。

因此一个n*n 块组成的平面, 有n*n 个平面法向量, 有4*n*n 个顶点法向量。

这两种法向量区别是, 如果材质的shading属性是THREE.SmoothShading 则采用顶点法向量, 如果不是则采用平面法向量, 平面法向量 导致整个面上的法向量处处相同,所以光照可能不够真实。


平面几何体的顶点数组是(n+1)*(n+1)的长度, 因此其法向量数组长度也应该是(n+1)*(n+1) 才合适, 而如果遍历面 将会产生4*n*n个向量, 如何修正这个问题呢?

平面几何体在绘制的过程中, 由sortFacesByMaterial 函数处理生成几何体组。

首先根据材质对几何体分组,

     材质编号_当前材质几何体组编号  作为几何体组的标识。

    接着将相应的平面块 压入到对应的几何体组中。

   控制每个几何体组的定点个数 小于 65535.


为几何体组编全局id号,  并将几何体组压入到 几何体组的List中

geometry.geometryGroups----->map形式访问几何体组

geometry.geometryGroupList-----> 数组形式访问几何体组


首先构建顶点 法向量 tangent, 颜色, 纹理坐标, 面, 线 等buffer。

接着初始化这些buffers。

接着在setMeshBuffers 中为这些buffer赋值, 根据每个独立的面都有将(n+1)*(n+1)个定点值写入到 4*n*n的顶点数组中去, 

用户自己定义的属性,如果按照点绑定,则根据面的数量将(n+1)*(n+1)个值写入到 4*n*n 长度的数组中。

如果按照面绑定则把 n*n 个值 写入到 4*n*n 个长度的数组中。


通过以上我们可以看到,绘制平面的时候, 虽然我们只写了(n+1)*(n+1)个定点值,但是引擎实际扩展到 4*n*n 个值,这样最大化了空间的使用,具有最大的灵活性。


知道了引擎的处理方法,我们构建一个(n+1)*(n+1)的shader属性,默认绑定在顶点上,接着计算向量值并赋值给这个属性就可以了。

材质如下:

var pmat = new THREE.ShaderMaterial({
        uniforms:{
            texture_grass:{type:'t', value:0, texture:THREE.ImageUtils.loadTexture("grassa512.bmp")},
            texture_rock:{type:'t', value:1, texture:THREE.ImageUtils.loadTexture("dirt512.bmp")},
            light:{type:'v3', value:new THREE.Vector3()},
            maxHeight:{type:'f', value:0},
            minHeight:{type:'f', value:1},
        },
        attributes:{
            displacement: {type:'f', value:[]},
            vexNormal:{type:'v3', value:[]},
        },
        vertexShader: document.getElementById("vert").textContent,
        fragmentShader: document.getElementById("frag").textContent,
    
    });

其中vertexNormal 就是逐顶点法向量,当然我们也可以直接修改默认每个面块的法向量或者修改平面法向量这两种方法都不方便,所以还是使用一个额外的属性来处理。

这个属性是v3 类型即对应的THREE数据类型是Vector3, 法向量的生成,对于每一个定点其左右定点连接的向量和上下顶点连接的向量的叉乘, 作为自身的法向量。


var v1 = new THREE.Vector3();
    var v2 = new THREE.Vector3();



    var distX = 2*3/(WIDTH-1);
    var distY = 2*3/(HEIGHT-1);
    
    var vexNormal = pmat.attributes.vexNormal.value;
    var vertices = pmesh.geometry.vertices;


    var lmat = new THREE.LineBasicMaterial({color:0xff0000});
    for(var i = 0; i < vertices.length; i++)
    {
        var row = ~~(i/WIDTH);
        var col = i%WIDTH;

        var left = (col-1+WIDTH)%WIDTH;
        var right = (col+1)%WIDTH;
        var up = (row-1+HEIGHT)%HEIGHT;
        var bottom = (row+1)%HEIGHT;

        var l = value[row*WIDTH+left];
        var r = value[row*WIDTH+right];
        v1.set(distX, 0, r-l);

        var u = value[up*WIDTH+col];
        var b = value[bottom*WIDTH+col];
        v2.set(0, distY, b-u);

        v1.crossSelf(v2.clone()).normalize();

        vexNormal.push(v1.clone());
        
        var lgeo = new THREE.Geometry();
        lgeo.vertices.push(new THREE.Vertex());
        lgeo.vertices.push(new THREE.Vertex(v1.clone()));

        var line = new THREE.Line(lgeo, lmat);
        line.position.set(vertices[i].position.x, vertices[i].position.y, value[i]);

        pmesh.add(line);
    }


这里计算的法向量是属于物体空间的, 在shader中我们需要将其转化成世界坐标, normalMatrix 是 世界视图modelView 矩阵的逆转置, 不能将法向量转化到世界坐标,因此,我们传入一个额外的矩阵, 当前引擎似乎只有mat4 的4*4 的矩阵, 因此我们传入4*4 objectMatrix 的逆转置。

normalWorldMatrix 是 要的矩阵。

var pmat = new THREE.ShaderMaterial({
        uniforms:{
            texture_grass:{type:'t', value:0, texture:THREE.ImageUtils.loadTexture("grassa512.bmp")},
            texture_rock:{type:'t', value:1, texture:THREE.ImageUtils.loadTexture("dirt512.bmp")},
            light:{type:'v3', value:new THREE.Vector3()},
            normalWorldMatrix:{type:'m4', value:new THREE.Matrix4()},
            maxHeight:{type:'f', value:0},
            minHeight:{type:'f', value:1},
        },
        attributes:{
            displacement: {type:'f', value:[]},
            vexNormal:{type:'v3', value:[]},
        },
        vertexShader: document.getElementById("vert").textContent,
        fragmentShader: document.getElementById("frag").textContent,
        //wireframe:true,
    
    });

将平面位置调整之后, updateMatrixWorld 更新平面的世界矩阵, 接着将平面的matrixWorld的逆转置赋值给normalWorldMatrix.

normalWorldmatrix.value.getInverse(pmesh.matrixWorld).transpose();


当然在shader里面我们只使用它的3*3 部分, 先将定点法向扩充成 4维 接着只取其前3维度即可。

nor = (normalWorldMatrix * vec4(vexNormal, 0)).xyz  


当然加入法向量的目的是 计算光照, 在平面上方设置一个光源位置 作为uniform传入 light.


lightDir = light-pos;

diffuse = max(dot(normalize(lightDir), nor), 0); 作为系数影响亮度。



本文转载自:http://blog.csdn.net/liyong748/article/details/7989872

共有 人打赏支持
李勇2

李勇2

粉丝 46
博文 189
码字总数 62209
作品 0
广州
程序员
three.js(六) 地形法向量生成

上一节采用 分形算法生成地形的高度值, 接着我们需要生成每个顶点的法向量。 three.js 的PlaneGeometry 自带有法向量, 法向量分为两种 即 平面法向量 和 平面每个定点法向量。 因此一个nn ...

李勇2
2012/09/18
0
0
three.js(八) bump map的生成

bump Map 主要用于增加表面的法向量细节。例如一个平面其法向量处处相同,即使使用了纹理,光照下的表现仍然不够真实。这时可以扰动表面面片的方向量,从而形成比较真实的光照效果。 类似于地...

李勇2
2012/09/18
0
0
three.js(五) 地形纹理混合

地形生成通常使用高度图, 而高度图的生成可以使用绘图工具,或者通过分形算法生成,例如square-diamond, fbm方法。 这里采用简单求平均值+随机波动的方法。 对于一个2^n+1 2^n+1 的网格, ...

李勇2
2012/09/18
0
0
three.js (四)离散层次细节level of details

LOD 处理比较大的外部地面场景中比较有用, 一般用于绘制地形。 首先通过可视体的切割删除不用的地形块,接着通过LOD 对照相机不同距离的地形块进行层次细节调整。 这里采用最简单的LOD 方法...

李勇2
2012/09/18
0
0
基于四叉树的LOD地形场景的怪异优化探索

基于四叉树的LOD地形场景是开发3D大规模场景地形一个避不开的话题。小弟对此一直耿耿于怀。虽然说号称自已学了几年游戏编程。但实际上真的还不会做四叉树的LOD地形。但眼下要么就干掉他,要么...

长平狐
2013/03/19
57
0

没有更多内容

加载失败,请刷新页面

加载更多

权限框架Shiro学习之表结构设计

权限框架Shiro学习之表结构设计 Shiro是一款优秀的开源安全框架,学习Shiro大家可以参考张开涛老师的博客:跟我学Shiro,当然也可参考我之前的笔记:Shiro实现身份认证、Shiro实现授权。 在学...

TyCoding
26分钟前
1
0
find命令和文件名后缀

9月18日任务 2.23/2.24/2.25 find命令 2.26 文件名后缀 which which 搜索可执行文件,必须在PATH环境变量目录中!!否则无法搜到! [root@centos7 ~]# which lsalias ls='ls --color=auto'...

robertt15
29分钟前
1
0
阿里Java程序员必备的Intellij IDEA 插件

善用Intellij插件可大幅提升我们的效率,以下是我用过不错的Intellij插件,分享给大家希望能帮到大家。 1. .ignore 生成各种ignore文件,一键创建git ignore文件的模板,免得自己去写 2. lom...

我是你大哥
39分钟前
1
0
为什么Java大神,都在看Spring Boot和Spring Cloud的书?

如果你是一名Java开发人员,并且最近正打算学习Spring Boot和Spring Cloud框架并寻找一些关于它们的最好的书籍,那么,你今天就来对地方了。 本文,我们将讨论一些学习Spring Boot和Spring ...

Java小铺
57分钟前
12
0
springboot logback日志配置

springboot 如果不使用外部tomcat的话,日志是需要自己配置的,不然的话就只有控制台的日志,但是日志又是我们在项目上了生产环境,出问题时,检查问题的唯一途径,所以我们要配置详细的日志...

曾大大胖
57分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部