在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ HTML/ WebGL 場(chǎng)景圖
WebGL 文本 HTML
WebGL 文本 Canvas 2D
WebGL 2D 圖像旋轉(zhuǎn)
WebGL 圖像處理(續(xù))
WebGL 2D 矩陣
WebGL 繪制多個(gè)東西
WebGL 圖像處理
WebGL 2D 圖像轉(zhuǎn)換
WebGL 3D 透視
WebGL 是如何工作的
WebGL 文本 紋理
WebGL 2D 圖像伸縮
WebGL 場(chǎng)景圖
WebGL 3D 攝像機(jī)
WebGL 文本 使用字符紋理
WebGL 正交 3D
WebGL 基本原理
WebGL - 更少的代碼,更多的樂趣
WebGL 著色器和 GLSL

WebGL 場(chǎng)景圖

我很肯定一些 CS 大師或者圖形大師會(huì)給我們講很多東西,但是...一個(gè)場(chǎng)景圖通常是一個(gè)樹結(jié)構(gòu),在這個(gè)樹結(jié)構(gòu)中的每個(gè)節(jié)點(diǎn)都生成一個(gè)矩陣...嗯,這并不是一個(gè)非常有用的定義。也許講一些例子會(huì)非常有用。

大多數(shù)的 3D 引擎都使用一個(gè)場(chǎng)景圖。你在場(chǎng)景圖中放置你想要在場(chǎng)景圖中出現(xiàn)的東西。引擎然后按場(chǎng)景圖行進(jìn),同時(shí)計(jì)算出需要繪制的一系列東西。場(chǎng)景圖都是有層次感的,例如,如果你想要去制作一個(gè)宇宙模擬圖,你可能需要一個(gè)圖與下面所示的圖相似

http://wiki.jikexueyuan.com/project/webgl/images/webgl-scene-graph1.png" alt="" />

一個(gè)場(chǎng)景圖的意義是什么?一個(gè)場(chǎng)景圖的 #1 特點(diǎn)是它為矩陣提供了一個(gè)父母子女關(guān)系,正如我們?cè)诙S矩陣數(shù)學(xué)中討論的。因此,例如在一個(gè)簡(jiǎn)單的宇宙中( 但是不是實(shí)際的 )模擬星星( 孩子 ),隨著它們的星系移動(dòng)( 父母 )。同樣,一個(gè)月亮( 孩子 )隨著行星移動(dòng),如果你移動(dòng)了地球,月亮?xí)黄鹨苿?dòng)。如果你移動(dòng)一個(gè)星系,在這個(gè)星系中的所有的星星也會(huì)隨著它一起移動(dòng)。在上面的圖中拖動(dòng)名稱,希望你可以看到它們之間的關(guān)系。

如果你回到二維矩陣數(shù)學(xué),你可能會(huì)想起我們將大量矩陣相乘來達(dá)到轉(zhuǎn)化,旋轉(zhuǎn)和縮放對(duì)象。一個(gè)場(chǎng)景圖提供了一個(gè)結(jié)構(gòu)來幫助決定要將哪個(gè)矩陣數(shù)學(xué)應(yīng)用到對(duì)象上。

通常,在一個(gè)場(chǎng)景圖中的每個(gè)節(jié)點(diǎn)都表示一個(gè)局部空間。給出了正確的矩陣數(shù)學(xué),在這個(gè)局部空間的任何東西都可以忽略在他上面的任何東西。用來說明同一件事的另一種方式是月亮只關(guān)心繞地球軌道運(yùn)行。它不關(guān)心繞太陽的軌道運(yùn)行。沒有場(chǎng)景圖結(jié)構(gòu),你需要做更多的復(fù)雜數(shù)學(xué),來計(jì)算怎樣才可以得到月亮繞太陽的軌道,因?yàn)樗@太陽的軌道看起來像這樣

http://wiki.jikexueyuan.com/project/webgl/images/webgl-scene-graph2.png" alt="" />

使用場(chǎng)景圖,你可以將月球看做是地球的孩子,然后簡(jiǎn)單的繞地球轉(zhuǎn)動(dòng)。場(chǎng)景圖很注意地球圍繞太陽轉(zhuǎn)的事實(shí)。它是通過節(jié)點(diǎn)和它走的矩陣相乘來完成的。

worldMatrix = greatGrandParent * grandParent * parent * self(localMatrix)

在具體的條款中,我們的宇宙模型可能是

worldMatrixForMoon = galaxyMatrix * starMatrix * planetMatrix * moonMatrix;

我們可以使用一個(gè)有效的遞歸函數(shù)來非常簡(jiǎn)單的完成這些

function computeWorldMatrix(currentNode, parentWorldMatrix) {
// compute our world matrix by multplying our local matrix with
// our parent's world matrix.
var worldMatrix = matrixMultiply(currentNode.localMatrix, parentWorldMatrix);

// now do the same for all of our children
currentNode.children.forEach(function(child) {
computeWorldMatrix(child, worldMatrix);
});
}

這將會(huì)給我們引進(jìn)一些在 3D 場(chǎng)景圖中非常常見的術(shù)語。

  • localMatrix:當(dāng)前節(jié)點(diǎn)的本地矩陣。它在原點(diǎn)轉(zhuǎn)換它和在局部空間它的孩子。

  • worldMatrix:對(duì)于給定的節(jié)點(diǎn),它需要獲取那個(gè)節(jié)點(diǎn)的局部空間的東西,同時(shí)將它轉(zhuǎn)換到場(chǎng)景圖的根節(jié)點(diǎn)的空間?;蛘?,換句話說,將它置于世界中。如果我們?yōu)樵虑蛴?jì)算世界矩陣,我們將會(huì)得到上面我們看到的軌道。

制作場(chǎng)景圖非常簡(jiǎn)單。讓我們定義一個(gè)簡(jiǎn)單的節(jié)點(diǎn)對(duì)象。還有無數(shù)個(gè)方式可以組織場(chǎng)景圖,我不確定哪一種方式是最好的。最常見的是有一個(gè)可以選擇繪制東西的字段。

 var node = {
   localMatrix: ...,  // the "local" matrix for this node
   worldMatrix: ...,  // the "world" matrix for this node
   children: [],  // array of children
   thingToDraw: ??,   // thing to draw at this node
};  

讓我們來做一個(gè)太陽系場(chǎng)景圖。我不準(zhǔn)備使用花式紋理或者類似的東西,因?yàn)樗鼤?huì)使例子變的混亂。首先讓我們來制作一些功能來幫助管理這些節(jié)點(diǎn)。首先我們將做一個(gè)節(jié)點(diǎn)類

var Node = function() {
  this.children = [];
  this.localMatrix = makeIdentity();
  this.worldMatrix = makeIdentity();
};

我們給出一種設(shè)置一個(gè)節(jié)點(diǎn)的父母的方式

Node.prototype.setParent = function(parent) {
  // remove us from our parent
  if (this.parent) {
var ndx = this.parent.children.indexOf(this);
if (ndx >= 0) {
  this.parent.children.splice(ndx, 1);
}
  }

  // Add us to our new parent
  if (parent) {
parent.children.append(this);
  }
  this.parent = parent;
};

這里,這里的代碼是從基于它們的父子關(guān)系的本地矩陣計(jì)算世界矩陣。如果我們從父母和遞歸訪問它孩子開始,我們可以計(jì)算它們的世界矩陣。

Node.prototype.updateWorldMatrix = function(parentWorldMatrix) {
  if (parentWorldMatrix) {
// a matrix was passed in so do the math and
// store the result in `this.worldMatrix`.
matrixMultiply(this.localMatrix, parentWorldMatrix, this.worldMatrix);
  } else {
// no matrix was passed in so just copy.
copyMatrix(this.localMatrix, this.worldMatrix);
  }

  // now process all the children
  var worldMatrix = this.worldMatrix;
  this.children.forEach(function(child) {
child.updateWorldMatrix(worldMatrix);
  });
}; 

讓我們僅僅做太陽,地球,月亮,來保持場(chǎng)景圖簡(jiǎn)單。當(dāng)然我們會(huì)使用假的距離,來使東西適合屏幕。我們將只使用一個(gè)單球體模型,然后太陽為淡黃色,地球?yàn)樗{(lán) - 淡綠色,月球?yàn)榈疑?。如果你?duì) drawInfo,bufferInfoprogramInfo 并不熟悉,你可以查看前一篇文章。

// Let's make all the nodes
var sunNode = new Node();
sunNode.localMatrix = makeTranslation(0, 0, 0);  // sun at the center
sunNode.drawInfo = {
  uniforms: {
u_colorOffset: [0.6, 0.6, 0, 1], // yellow
u_colorMult:   [0.4, 0.4, 0, 1],
  },
  programInfo: programInfo,
  bufferInfo: sphereBufferInfo,
};

var earthNode = new Node();
earthNode.localMatrix = makeTranslation(100, 0, 0);  // earth 100 units from the sun
earthNode.drawInfo = {
  uniforms: {
u_colorOffset: [0.2, 0.5, 0.8, 1],  // blue-green
u_colorMult:   [0.8, 0.5, 0.2, 1],
  },
  programInfo: programInfo,
  bufferInfo: sphereBufferInfo,
};

var moonNode = new Node();
moonNode.localMatrix = makeTranslation(20, 0, 0);  // moon 20 units from the earth
moonNode.drawInfo = {
  uniforms: {
u_colorOffset: [0.6, 0.6, 0.6, 1],  // gray
u_colorMult:   [0.1, 0.1, 0.1, 1],
  },
  programInfo: programInfo,
  bufferInfo: sphereBufferInfo,
};

現(xiàn)在我們已經(jīng)得到了節(jié)點(diǎn),讓我們來連接它們。

// connect the celetial objects
moonNode.setParent(earthNode);
earthNode.setParent(sunNode);

我們會(huì)再一次做一個(gè)對(duì)象的列表和一個(gè)要繪制的對(duì)象的列表。

var objects = [
  sunNode,
  earthNode,
  moonNode,
];

var objectsToDraw = [
  sunNode.drawInfo,
  earthNode.drawInfo,
  moonNode.drawInfo,
];

在渲染時(shí),我們將會(huì)通過稍微旋轉(zhuǎn)它來更新每一個(gè)對(duì)象的本地矩陣。

// update the local matrices for each object.
matrixMultiply(sunNode.localMatrix, makeYRotation(0.01), sunNode.localMatrix);
matrixMultiply(earthNode.localMatrix, makeYRotation(0.01), earthNode.localMatrix);
matrixMultiply(moonNode.localMatrix, makeYRotation(0.01), moonNode.localMatrix);

現(xiàn)在,本地矩陣都更新了,我們會(huì)更新所有的世界矩陣。

sunNode.updateWorldMatrix();

最后,我們有了世界矩陣,我們需要將它們相乘來為每個(gè)對(duì)象獲取一個(gè)世界觀投射矩陣。

// Compute all the matrices for rendering
objects.forEach(function(object) {
  object.drawInfo.uniforms.u_matrix = matrixMultiply(object.worldMatrix, viewProjectionMatrix);
});

渲染是我們?cè)谏弦黄恼轮锌吹降南嗤难h(huán)。

你將會(huì)注意到所有的行星都是一樣的尺寸。我們?cè)囍尩厍蚋簏c(diǎn)。

earthNode.localMatrix = matrixMultiply(
makeScale(2, 2, 2),   // make the earth twice as large
makeTranslation(100, 0, 0));  // earth 100 units from the sun

哦。月亮也越來越大。為了解決這個(gè)問題,我們可以手動(dòng)的縮小月亮。但是一個(gè)更好的解決方法是在我們的場(chǎng)景圖中增加更多的節(jié)點(diǎn)。而不僅僅是如下圖所示。

  sun
   |
  earth
   |
  moon

我們將改變它為

 solarSystem
   ||
   |   sun
   |
 earthOrbit
   ||
   |  earth
   |
  moonOrbit
  |
 moon

這將會(huì)使地球圍繞太陽系旋轉(zhuǎn),但是我們可以單獨(dú)的旋轉(zhuǎn)和縮放太陽,它不會(huì)影響地球。同樣,地球與月球可以單獨(dú)旋轉(zhuǎn)。讓我們給太陽系,地球軌道月球軌道設(shè)置更多的節(jié)點(diǎn)。

var solarSystemNode = new Node();
var earthOrbitNode = new Node();
earthOrbitNode.localMatrix = makeTranslation(100, 0, 0);  // earth orbit 100 units from the sun
var moonOrbitNode = new Node();
moonOrbitNode.localMatrix = makeTranslation(20, 0, 0);  // moon 20 units from the earth

這些軌道距離已經(jīng)從舊的節(jié)點(diǎn)移除

http://wiki.jikexueyuan.com/project/webgl/images/webgl-scene-graph5.png" alt="" />

現(xiàn)在連接它們,如下所示

// connect the celetial objects
sunNode.setParent(solarSystemNode);
earthOrbitNode.setParent(solarSystemNode);
earthNode.setParent(earthOrbitNode);
moonOrbitNode.setParent(earthOrbitNode);
moonNode.setParent(moonOrbitNode);

同時(shí),我們只需要更新軌道

http://wiki.jikexueyuan.com/project/webgl/images/webgl-scene-graph6.png" alt="" />

現(xiàn)在你可以看到地球是兩倍大小,而月球不會(huì)。

你可能還會(huì)注意到太陽和地球不再旋轉(zhuǎn)到位。它們現(xiàn)在是無關(guān)的。

讓我們調(diào)整更多的東西。

http://wiki.jikexueyuan.com/project/webgl/images/webgl-scene-graph8.png" alt="" />

目前我們有一個(gè) localMatrix,我們?cè)诿恳粠夹薷乃5怯幸粋€(gè)問題,即在每一幀中我們數(shù)學(xué)都將收集一點(diǎn)錯(cuò)誤。有許多可以解決這種被稱為鄰位的正?;仃?/em>的數(shù)學(xué)的方式,但是,甚至是它都不總是奏效。例如,讓我們想象我們縮減零。讓我們?yōu)橐粋€(gè)值 x 這樣做。

x = 246;   // frame #0, x = 246

scale = 1;
x = x * scale  // frame #1, x = 246

scale = 0.5;
x = x * scale  // frame #2, x = 123

scale = 0;
x = x * scale  // frame #3, x = 0

scale = 0.5;
x = x * scale  // frame #4, x = 0  OOPS!

scale = 1;
x = x * scale  // frame #5, x = 0  OOPS!

我們失去了我們的值。我們可以通過添加其他一些從其他值更新矩陣的類來解決它。讓我們通過擁有一個(gè) source 來改變 Node 的定義。如果它存在,我們會(huì)要求 source 給出我們一個(gè)本地矩陣。

http://wiki.jikexueyuan.com/project/webgl/images/webgl-scene-graph10.png" alt="" />

現(xiàn)在我們來創(chuàng)建一個(gè)源。一個(gè)常見的源是那些提供轉(zhuǎn)化,旋轉(zhuǎn)和縮放的,如下所示。

var TRS = function() {
  this.translation = [0, 0, 0];
  this.rotation = [0, 0, 0];
  this.scale = [1, 1, 1];
};

TRS.prototype.getMatrix = function(dst) {
  dst = dst || new Float32Array(16);
  var t = this.translation;
  var r = this.rotation;
  var s = this.scale;

  // compute a matrix from translation, rotation, and scale
  makeTranslation(t[0], t[1], t[2], dst);
  matrixMultiply(makeXRotation(r[0]), dst, dst);
  matrixMultiply(makeYRotation(r[1]), dst, dst);
  matrixMultiply(makeZRotation(r[2]), dst, dst);
  matrixMultiply(makeScale(s[0], s[1], s[2]), dst, dst);
  return dst;
};

我們可以像下面一樣使用它

// at init time making a node with a source
var someTRS  = new TRS();
var someNode = new Node(someTRS);

// at render time
someTRS.rotation[2] += elapsedTime;

現(xiàn)在沒有問題了,因?yàn)槲覀兠看味贾匦聞?chuàng)建矩陣。

你可能會(huì)想,我沒做一個(gè)太陽系,所以這樣的意義何在?好吧,如果你想要去動(dòng)畫一個(gè)人,你可能會(huì)有一個(gè)跟下面所示一樣的場(chǎng)景圖。

http://wiki.jikexueyuan.com/project/webgl/images/webgl-scene-graph11.png" alt="" />

為手指和腳趾添加多少關(guān)節(jié)全部取決于你。你有的關(guān)節(jié)越多,它用于計(jì)算動(dòng)畫的力量越多,同時(shí)它為所有的關(guān)節(jié)提供的動(dòng)畫數(shù)據(jù)越多。像虛擬戰(zhàn)斗機(jī)的舊游戲大約有 15 個(gè)關(guān)節(jié)。在 2000 年代早期至中期,游戲有 30 到 70 個(gè)關(guān)節(jié)。如果你為每個(gè)手都設(shè)置關(guān)節(jié),在每個(gè)手中至少有 20 個(gè),所以兩只手是 40 個(gè)關(guān)節(jié)。許多想要?jiǎng)赢嬍值挠螒蚨及汛竽粗柑幚頌橐粋€(gè),其他的四個(gè)作為一個(gè)大的手指處理,以節(jié)省時(shí)間( 所有的 CPU/GPU 和藝術(shù)家的時(shí)間 )和內(nèi)存。

不管怎樣,這是一個(gè)我組件在一起的塊人。它為上面提到的每個(gè)節(jié)點(diǎn)使用 TRS 源。藝術(shù)程序員和動(dòng)畫程序員萬歲。

如果你查看一下,幾乎所有的 3D 圖書館,你都會(huì)發(fā)現(xiàn)一個(gè)與下圖類似的場(chǎng)景圖。