文档章节

three.js 地形法向量生成

李勇2
 李勇2
发布于 2015/03/02 09:38
字数 1264
阅读 45
收藏 0
点赞 0
评论 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

粉丝 45
博文 188
码字总数 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
基于四叉树的LOD地形场景的怪异优化探索

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

长平狐
2012/11/19
92
0
WebGL多模型光照综合实例

  原文地址:WebGL多模型光照综合实例   WebGL是一个非常的接近硬件底层的光栅化API, 从非常类似C/C++风格的API调用方式就可以看出来, 习惯了高级语言的我们会觉得很不友好,觉得特别繁琐...

jeffzhong
01/12
0
0
[译文] 如何高效渲染庞大的地形 - Rendering large terrains

翻译:RyuZhihao123 时间:2017/5/16(大三下学期) 原文链接:Render large terrains - pheelicks Rendering large terrains Today we’ll look at how to efficiently render a large terr......

Mahabharata_
2017/05/17
0
0
从0开始的OpenGL学习(二十五)

本章主要解决1个问题: 如何在OpenGL使用几何着色器? 引言 除了顶点着色器和片元着色器,OpenGL还提供了几个额外的着色器可供使用,本章讲的几何着色器(geometry shader)就是其中之一。几...

闪电的蓝熊猫
2017/12/06
0
0
【three.js】库

three.js 一个轻量级的webgl库,但是十分强大。 下载地址https://github.com/mrdoob/three.js OrbitControls.js 控制视口的平移、缩放、旋转。 GridHelper.js 生成视口的网格。...

騡月
2016/03/20
74
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

NNS域名系统之域名竞拍

0x00 前言 其实在官方文档中已经对域名竞拍的过程有详细的描述,感兴趣的可以移步http://doc.neons.name/zh_CN/latest/nns_protocol.html#id30 此处查阅。 我这里主要对轻钱包开发中会用到的...

暖冰
今天
0
0
32.filter表案例 nat表应用 (iptables)

10.15 iptables filter表案例 10.16/10.17/10.18 iptables nat表应用 10.15 iptables filter表案例: ~1. 写一个具体的iptables小案例,需求是把80端口、22端口、21 端口放行。但是,22端口我...

王鑫linux
今天
0
0
shell中的函数&shell中的数组&告警系统需求分析

20.16/20.17 shell中的函数 20.18 shell中的数组 20.19 告警系统需求分析

影夜Linux
今天
0
0
Linux网络基础、Linux防火墙

Linux网络基础 ip addr 命令 :查看网口信息 ifconfig命令:查看网口信息,要比ip addr更明了一些 centos 7默认没安装ifconfig命令,可以使用yum install -y net-tools命令来安装。 ifconfig...

李超小牛子
今天
1
0
[机器学习]回归--Decision Tree Regression

CART决策树又称分类回归树,当数据集的因变量为连续性数值时,该树算法就是一个回归树,可以用叶节点观察的均值作为预测值;当数据集的因变量为离散型数值时,该树算法就是一个分类树,可以很...

wangxuwei
昨天
1
0
Redis做分布式无锁CAS的问题

因为Redis本身是单线程的,具备原子性,所以可以用来做分布式无锁的操作,但会有一点小问题。 public interface OrderService { public String getOrderNo();} public class OrderRe...

算法之名
昨天
10
0
143. Reorder List - LeetCode

Question 143. Reorder List Solution 题目大意:给一个链表,将这个列表分成前后两部分,后半部分反转,再将这两分链表的节点交替连接成一个新的链表 思路 :先将链表分成前后两部分,将后部...

yysue
昨天
1
0
数据结构与算法1

第一个代码,描述一个被称为BankAccount的类,该类模拟了银行中的账户操作。程序建立了一个开户金额,显示金额,存款,取款并显示余额。 主要的知识点联系为类的含义,构造函数,公有和私有。...

沉迷于编程的小菜菜
昨天
1
0
从为什么别的队伍总比你的快说起

在机场候检排队的时候,大多数情况下,别的队伍都要比自己所在的队伍快,并常常懊悔当初怎么没去那个队。 其实,最快的队伍只能有一个,而排队之前并不知道那个队快。所以,如果有六个队伍你...

我是菜鸟我骄傲
昨天
1
0
分布式事务常见的解决方案

随着互联网的发展,越来越多的多服务相互之间的调用,这时候就产生了一个问题,在单项目情况下很容易实现的事务控制(通过数据库的acid控制),变得不那么容易。 这时候就产生了多种方案: ...

小海bug
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部