Three.js的绘制流程(三)----地形
2019獨角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
最簡單的地形是單一的平面, 這個通過
var geo = new THREE.PlaneGeometry(2, 2, 256, 256) 幾何體構(gòu)建, 可以設(shè)定平面的切分塊的數(shù)量。
var pmesh = new THREE.Mesh(geo, material);
可以為平面提供紋理, 從而是地面看起來更真實一些,而紋理坐標(biāo)在geo中已經(jīng)自動設(shè)定好了。 因此只需要寫材質(zhì)就可以了。
這里使用ShaderMaterial 用于材質(zhì)。
頂點shader:
varying vec2 vUV; //從定點shader 傳遞到 片段shader的紋理坐標(biāo)
void main()
{
? ? ? vUV = uv; //uv 是默認(rèn)存在的頂點屬性, 用于做紋理坐標(biāo)
? ? ? gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1);// projectionMatrix 默認(rèn)存在的投影矩陣 modelViewMatrix默認(rèn)模型視圖矩陣 position定點坐標(biāo)?
}
?
片段shader:
uniform sampler2D texture_grass;//紋理
varying vec2 vUV;
void main()
{
? ? ?gl_FragColor = texture2D(texture_grass, vUV);
}
材質(zhì)則是:
var mat = new THREE.ShaderMaterial({
? ? ?uniforms:{
? ? ? ? ? texture_grass:{type:'t', value:0, ?texture:TRHEE.ImageUtils.loadTexture("texture_grass.png")},//類型紋理, 紋理編號0, 加載圖片數(shù)據(jù)?
? ? },
? ? ?attributes:{},
? ? ?vertexShader:text,
? ? ?fragmentShader:text,
});
var pmesh = new THREE.Mesh(geo, mat);
?
這樣就得到一個添加了紋理的平面, 平面在x,y 平面上, 平面的正方向指向z正向, 可以調(diào)整平面x軸逆時針旋轉(zhuǎn)90度, 得到x,z 方向的平面。
?
現(xiàn)在我們期望能夠調(diào)整地面的高度, 采用的方式是鼠標(biāo)點擊屏幕位置, 計算實際的地面的位置, 接著調(diào)整地面上該點附近的所有定點的高度。
調(diào)整高度有兩種方式, 一是調(diào)整geo幾何體中的vertices 中的y值, 或者在修改shader程序 增加一個頂點屬性, displacement 用于記錄所有頂點的高度,這樣只需要修改這一個數(shù)據(jù)就可以了,而不需要修改整個定點數(shù)組。
頂點shader調(diào)整:
attribute float displacement;
varying vec2 vUV;
void main()
{
? ? ? ?vUV = uv;
? ? ? ?gl_Position = projectionMatrix * modelViewMatrix * vec4(position.xy, ?displacement, 1);
}
材質(zhì)調(diào)整:
var mat = new THREE.ShaderMaterial({
? ? ? //其它部分相同
? ? ? ?attributes:{
? ? ?displacement:{type:'f', ?value:[]},
? ?}
});
需要根據(jù)平面頂點個數(shù) 來初始化displacement值為0
for(var i = 0; i < geo.vertices.length; i++)
? ? ? mat.attributes.displacement.value.push(0);
?
因為需要動態(tài)調(diào)整displacement的值 所以需要設(shè)定geo是動態(tài)更新的
geo.dynamic = true;
?
這樣現(xiàn)在仍只是看到一個平面;接下來需要設(shè)定鼠標(biāo)點擊的時間處理函數(shù)。
var projector = new THREE.Projector(); //計算從屏幕坐標(biāo)到 世界坐標(biāo)的工具
?
renderer.domElement.onmousedown = function(e){
? ? ?var mx = e.offsetX;
? ? ?var my = e.offsetY;
? ? ?//計算相機(jī)近平面上的坐標(biāo)
? ? ? mx = 2*mx/SCREEN_WIDTH-1;
? ? ?my = 1 - 2*my/SCREEN_HEIGHT;
? ? var vec = new THREE.Vector3(mx, my, 0);//只有 x y 坐標(biāo)有意義
? ? ?var ray = project.pickingRay(vec, camera);//計算從相機(jī)發(fā)出的穿過近平面上mx my 點的射線
? ? var intersects = ray.intersectObject(pmesh); //計算射線和 平面的所有交點 0 或者 1個
? ? ?if(intersects.length > 0)
? ? {
? ? ? ? ? ? //存在交點
? ? ? ? ? ?updateDisplacement(intersects[0].point); //調(diào)整該點附近頂點的高度。
? ? }
}
?
function updateDisplacement(point)
{
//平面方位 -1, 1 ?-1, 1 x軸切分256份, WIDTH= 257, ?y軸切分256份 , HEIGHT=257
//計算交點附近 0.1為邊長的矩形框的上下左右范圍, 接著計算geo中相應(yīng)的定點的行和列范圍。
//調(diào)整所有頂點的高度, 設(shè)定材質(zhì)中的 mat.attributes.displacement.needsUpdate = true; ?設(shè)定displacement需要更新
?
var radius = 0.05; var left = point.x-radius; var right = point.x+radius; var bottom = point.z-radius; var up = point.z + radius; var rowGrid = 2/(HEIGHT-1); var colGrid = 2/(WIDTH-1); var rowBegin = ~~Math.max((bottom+1)/rowGrid, 0); var rowEnd = ~~Math.min((up+1)/rowGrid, HEIGHT); var colBegin = ~~Math.max((left+1)/colGrid, 0); var colEnd = ~~Math.min((right+1)/colGrid, WIDTH); for(var i = rowBegin; i <= rowEnd; i++) { for(var j = colBegin; j <= colEnd; j++) { var num = ~~(i*WIDTH+j); //console.log(num); pmesh.displacement.value[num] += 0.05; //console.log("new", pmesh.displacement, pmesh.displacement.needsUpdate); } } pmesh.displacement.needsUpdate = true;
}
?
結(jié)束:
? ?通過設(shè)定幾何體可以動態(tài)更新, ?設(shè)定 材質(zhì)中的屬性需要更新, 則實現(xiàn)了調(diào)整displacement的目的。
轉(zhuǎn)載于:https://my.oschina.net/u/186074/blog/376765
總結(jié)
以上是生活随笔為你收集整理的Three.js的绘制流程(三)----地形的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PHP学习笔记——Php文件引入
- 下一篇: 教你利用python处理文本