文章來源:http://www.freegames.com.cn/school/383/2007/27685.html
Nemesis2k
per-pixel lighting 紋理空間坐標基的計算方法
我知道的幾種方法:
1. 對于參數化的表面,設其方程為 P = P (u, v),其中 P 為向量,
三個分量分別為 x, y z。也可以表示為:
Px = Px (u ,v)
Py = Py (u ,v)
Pz = Pz (u ,v)
那在任意一個頂點
T = {dPx/du, dPy/du, dPz/du}
B = {dPx/dv, dPy/dv, dPz/dv}
N = T X B
然后把 T, B, N 歸一化就行了。
這里的偏導數可以用差分計算。
這樣計算出來的切空間是在每一個頂點的切空間。
2。對于由三角形面片組成的網格,在 MSDN 上的 Per-pixel lighting
文章里介紹了一種方法。
設三角形的三個頂點是 P0, P1, P2,其中每個頂點都有位置,法向量
和 2-D 紋理坐標。
Pi : {x, y, z}, {nx, ny, nz}, {s, t}
現在我們要計算在 P0 點的切空間。
這里要分辨兩個切空間:
1)頂點上的切空間
2)三角形面片上的切空間
兩個切空間是相同的嗎?我覺得是不同的。方法 2 和方法 3 計算出來的
實際上都是三角形面片的切空間,頂點的切空間還要通過平均頂點所在
各個三角形面片的切空間基向量來計算。(是這樣的嗎?高手指教一下!)
設三角形面片所在的切空間的基向量為 T, B, N,坐標原點在 P0。
那么三角形面片中的任意向量應該可以表示為:
Vec = x*T + y*B
因此,如果我們找到了兩個向量 Vec1, Vec2 以及它們在 T, B 上的
分量,那么自然就可以解出 T, B 了。
令:
Vec1 = P1 - P0
Vec2 = P2 - P0
dS1 = P1.s - P0.s
dS2 = P2.s - P0.s
dT1 = P1.t - P0.t
dT2 = P2.t - p0.t
那么我們有
Vec1 = dS1*T + dT1*B (1)
Vec2 = dS2*T + dT2*B (2)
聯立 (1), (2) 就可以解出
B*(dS2*dT1 - dS1*dT2) = (dS2*Vec1 - dS1*Vec2)
所以:
(dS2*dT1 - dS1*dT2) 是一個常數,反正我們之后要對 B 歸一化,
可以不用管它。于是:
B = normalize(dS2*Vec1 - dS1*Vec2) 這就是 MSDN 里那篇文章里的方法。
B 可以通過解方程獲得,但是 T 就不行了,因為這樣解出來的 T 和
B 不一定垂直。怎么處理呢?
MSDN 中的方法是,利用頂點的 N 來求 T:
T = B X N
然后再求 N
N = T X B
但是這樣可以嗎?這里的 N 是頂點 P0 的 N,而不是三角形面片的 N。
是不是這樣求出來的 T, N, B 恰好是頂點 P0 的切空間的坐標基,不需要
再平均了?(高手指教!)
我想的處理方法是這樣的:
同樣解出 T 來:
T = normalize(dT2*Vec1 - dT1*Vec2)
然后 N = T X B。這個 N 是三角形面片的 N。
然后 T = B X N。這樣 T, N, B 構成正交基,而且是三角形面片的。
要計算 P0 頂點的切空間基,還需要平均多個面片。
這種方法到是比較復雜。
一個問題是,為什么有
Vec1 = dS1*T + dT1*B (1)
Vec2 = dS2*T + dT2*B (2)
這兩個公式!
我想是因為在計算頂點的紋理坐標時,因為是從平面映射到平面,所以我們使用了
仿射變換:
s = as*x + bs*y + cs
t = as*x + bs*y + cs
反過來我們有
x = ax*s + bx*t + cx (3)
y = ay*s + by*t + cy (4)
z = az*s + bz*t + cz (5)
于是
Vec1.x = P1.x - P0.x = ax*(P1.s - P0.s) + bx*(P1.t - P0.t)
Vec1.y = P1.y - P0.y = ay*(P1.s - P0.s) + by*(P1.t - P0.t)
Vec1.z = P1.z - P0.z = az*(P1.s - P0.s) + bz*(P1.t - P0.t)
于是
Vec1 = {ax, ay, az}*dS1 + {bx, by, bz}*dT1
這和 (1) 已經很象了,那么 {ax, ay, az} 就是 T 嗎?
答案是是的!事實上 (3), (4), (5) 就是三角形面片的參數表示,那么
T = {dx/ds, dy/ds, dz/ds} = {ax, ay, az}
B = {dx/dt, dy/dt, dz/dt} = {bx, by, bz}
也就是說,如果我們能直接把 ax, ay, az, bx, by, bz 求出來,T 和 B 就求出來了
(當然要把他們正交歸一化)
3 nVidia 網站上的方法。
我們可以假設
x = ax*s + bx*t + cx
y = ay*s + by*t + cy
z = az*s + bz*t + cz
如何求解 (3) ?這里有 3 個未知數,那我們需要 3 個方程。
將 3 個頂點的屬性 {x, y ,z}, {s, t} 帶入,剛好有三個方程:
P0.x = ax*P0.s + bx*P0.t + cx (1)
P1.x = ax*P1.s + bx*P1.t + cx (2)
P2.x = ax*P2.s + bx*P2.t + cx (3)
解出來就得到 ax, bx, cx 了。
同理可得: ay, by, cy, az, bz, cz。
T = {ax, ay, az}
B = {bx, by, bz}
N = T X B
T = B X N
然后都歸一化即可。
nVidia 網站上的方法呢,是建立三個平面方程
Ax*x + Bx*s + Cx*t + Dx = 0 (4)
Ay*y + By*s + Cy*t + Dy = 0 (5)
Az*z + Bz*s + Cz*t + Dz = 0 (6)
并且指出,三角形面片上的所有點的 (x, s, t) 都在
方程 (4) 定義的平面中。那么
dx/ds = -Bx/Ax
dx/dt = -Cx/Ax
同理
dy/ds = -By/Ay
dy/dt = -Cy/Ay
dz/ds = -Bz/Az
dz/dt = -Cz/Az
那么這些 Ax, Ay, Az, Bx, By, Bz, Cx, Cy, Cz 怎么求呢?
容易知道,{Ax, Bx, Cx} 其實是平面的法向量,那么可以
選平面中的三個點,計算出兩個向量,然后叉乘。
{Ax, Bx, Cx} = {P1.x - P0.x, P1.s - P0.s, P1.t - P0.t} X
{P2.x - P0.x, P2.s - P0.s, P2.t - P0.t}
這兩種方法是等價的。
API Call |
Average number of Cycles |
SetVertexDeclaration |
6500 - 11250 |
SetFVF |
6400 - 11200 |
SetVertexShader |
3000 - 12100 |
SetPixelShader |
6300 - 7000 |
SPECULARENABLE |
1900 - 11200 |
SetRenderTarget |
6000 - 6250 |
SetPixelShaderConstant (1 Constant) |
1500 - 9000 |
NORMALIZENORMALS |
2200 - 8100 |
LightEnable |
1300 - 9000 |
SetStreamSource |
3700 - 5800 |
LIGHTING |
1700 - 7500 |
DIFFUSEMATERIALSOURCE |
900 - 8300 |
AMBIENTMATERIALSOURCE |
900 - 8200 |
COLORVERTEX |
800 - 7800 |
SetLight |
2200 - 5100 |
SetTransform |
3200 - 3750 |
SetIndices |
900 - 5600 |
AMBIENT |
1150 - 4800 |
SetTexture |
2500 - 3100 |
SPECULARMATERIALSOURCE |
900 - 4600 |
EMISSIVEMATERIALSOURCE |
900 - 4500 |
SetMaterial |
1000 - 3700 |
ZENABLE |
700 - 3900 |
WRAP0 |
1600 - 2700 |
MINFILTER |
1700 - 2500 |
MAGFILTER |
1700 - 2400 |
SetVertexShaderConstant (1 Constant) |
1000 - 2700 |
COLOROP |
1500 - 2100 |
COLORARG2 |
1300 - 2000 |
COLORARG1 |
1300 - 1980 |
CULLMODE |
500 - 2570 |
CLIPPING |
500 - 2550 |
DrawIndexedPrimitive |
1200 - 1400 |
ADDRESSV |
1090 - 1500 |
ADDRESSU |
1070 - 1500 |
DrawPrimitive |
1050 - 1150 |
SRGBTEXTURE |
150 - 1500 |
STENCILMASK |
570 - 700 |
STENCILZFAIL |
500 - 800 |
STENCILREF |
550 - 700 |
ALPHABLENDENABLE |
550 - 700 |
STENCILFUNC |
560 - 680 |
STENCILWRITEMASK |
520 - 700 |
STENCILFAIL |
500 - 750 |
ZFUNC |
510 - 700 |
ZWRITEENABLE |
520 - 680 |
STENCILENABLE |
540 - 650 |
STENCILPASS |
560 - 630 |
SRCBLEND |
500 - 685 |
Two_Sided_StencilMODE |
450 - 590 |
ALPHATESTENABLE |
470 - 525 |
ALPHAREF |
460 - 530 |
ALPHAFUNC |
450 - 540 |
DESTBLEND |
475 - 510 |
COLORWRITEENABLE |
465 - 515 |
CCW_STENCILFAIL |
340 - 560 |
CCW_STENCILPASS |
340 - 545 |
CCW_STENCILZFAIL |
330 - 495 |
SCISSORTESTENABLE |
375 - 440 |
CCW_STENCILFUNC |
250 - 480 |
SetScissorRect |
150 - 340 |
使用D3D,我們就得知道常用的API的消耗,才能夠方便我們優化自己的渲染器。這里給出了常用API的消耗表,可以有一個直觀的比較。
這個表也可以在D3D SDK文檔的 Accurately Profiling Direct3D API Calls (Direct3D 9) 一文中找到
Table 6.12. Constructors and Destructor of Lists Operation Effect
list<Elem> c Creates an empty list without any elements
list<Elem> c1(c2) Creates a copy of another list of the same type (all elements are copied)
list<Elem> c(n) Creates a list with n elements that are created by the default constructor
list<Elem> c(n,elem) Creates a list initialized with n copies of element elem
list<Elem> c (beg,end) Creates a list initialized with the elements of the range [beg,end)
c.~list<Elem>() Destroys all elements and frees the
memory
Table 6.13. Nonmodifying Operations of Lists Operation Effect
c.size() Returns the actual number of elements
c. empty () Returns whether the container is empty (equivalent to size()==0, but might be faster)
c.max_size() Returns the maximum number of elements possible
c1 == c2 Returns whether c1 is equal to c2
c1 != c2 Returns whether c1 is not equal to c2 (equivalent to ! (c1==c2))
c1 < c2 Returns whether c1 is less than c2
c1 > c2 Returns whether c1 is greater than c2 (equivalent to c2<c1)
c1 <= c2 Returns whether c1 is less than or equal to c2 (equivalent to ! (c2<c1) )
c1 >= c2 Returns whether c1 is greater than or equal to c2 (equivalent to ! (c1<c2))
Table 6.14. Assignment Operations of Lists Operation Effect
c1 = c2 Assigns all elements of c2 to c1
c.assign(n,elem) Assigns n copies of element elem
c.assign(beg,end) Assigns the elements of the range [beg,end)
c1.swap(c2) Swaps the data of c1 and c2
swap(c1,c2) Same (as global function)
Table 6.15. Direct Element Access of Lists Operation Effect
c.front() Returns the first element (no check whether a first element exists)
c.back() Returns the last element (no check whether a last element exists)
Table 6.16. Iterator Operations of Lists Operation Effect
c.begin() Returns a bidirectional iterator for the first element
c.end() Returns a bidirectional iterator for the position after the last element
c.rbegin() Returns a reverse iterator for the first element of a reverse iteration
c.rend() Returns a reverse iterator for the position after the last element of a reverse iteration
Table 6.17. Insert and Remove Operations of Lists Operation Effect
c.insert (pos, elem) Inserts at iterator position pos a copy of elem and returns the position of the new element
c.insert (pos,n, elem) Inserts at iterator position pos n copies of elem (returns nothing)
c. insert (pos, beg,end) Inserts at iterator position pos a copy of all elements of the range [beg,end) (returns nothing)
c.push_back(elem) Appends a copy of elem at the end
c.pop_back() Removes the last element (does not return it)
c.push_front(elem) Inserts a copy of elem at the beginning
c.pop_front () Removes the first element (does not return it)
c. remove (val) Removes all elements with value val
c.remove_if (op) Removes all elements for which op(elem) yields true
c. erase (pos) Removes the element at iterator position pos and returns the position of the next element
c.erase (beg,end) Removes all elements of the range [beg,end) and returns the position of the next element
c. resize (num) Changes the number of elements to num (if size() grows, new elements are created by their default constructor)
c.resize (num, elem) Changes the number of elements to num (if size ( ) grows, new elements are copies of elem)
c. clear () Removes all elements (makes the container empty)
Table 6.18. Special Modifying Operations for Lists Operation Effect
c.unique() Removes duplicates of consecutive elements with the same value
c.unique(op) Removes duplicates of consecutive elements, for which op() yields true
c1.splice(pos,c2) Moves all elements of c2 to c1 in front of the iterator position pos
c1.splice(pos,c2,c2pos) Moves the element at c2pos in c2 in front of pos of list c1 (c1 and c2 may be identical)
c1.splice(pos,c2,c2beg,c2end) Moves all elements of the range [c2beg,c2end) in c2 in front of pos of list c1 (c1 and c2 may be identical)
c.sort() Sorts all elements with operator <
c.sort(op) Sorts all elements with op()
c1.merge(c2) Assuming both containers contain the elements sorted, moves all elements of c2 into c1 so that all elements are merged and still sorted
c1.merge(c2,op) Assuming both containers contain the elements sorted due to the sorting criterion op(), moves all elements of c2 into c1 so that all elements are merged and still sorted according to op()
c.reverse() Reverses the order of all elements
Examples of Using Lists
The following example in particular shows the use of the special member functions for lists:
// cont/list1.cpp
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
void printLists (const list<int>& 11, const list<int>& 12)
{
cout << "list1: ";
copy (l1.begin(), l1.end(), ostream_iterator<int>(cout," "));
cout << endl << "list2: ";
copy (12.begin(), 12.end(), ostream_iterator<int>(cout," "));
cout << endl << endl;
}
int main()
{
//create two empty lists
list<int> list1, list2;
//fill both lists with elements
for (int i=0; i<6; ++i) {
list1.push_back(i);
list2.push_front(i);
}
printLists(list1, list2);
//insert all elements of list1 before the first element with value 3 of list2
//-find() returns an iterator to the first element with value 3
list2.splice(find(list2.begin(),list2.end(), // destination position
3),
list1); // source list
printLists(list1, list2);
//move first element to the end
list2.splice(list2.end(), // destination position
list2, // source list
list2.begin()); // source position
printLists(list1, list2);
//sort second list, assign to list1 and remove duplicates
list2.sort();
list1 = list2;
list2.unique();
printLists(list1, list2);
//merge both sorted lists into the first list
list1.merge(list2);
printLists(list1, list2);
}
The program has the following output:
list1: 0 1 2 3 4 5
list2: 5 4 3 2 1 0
list1:
list2: 5 4 0 1 2 3 4 5 3 2 1 0
list1:
list2: 4 0 1 2 3 4 5 3 2 1 0 5
list1: 0 0 1 1 2 2 3 3 4 4 5 5
list2: 0 1 2 3 4 5
list1: 0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5
list2:
Vector的
//建立一個向量并為之分配內存
std::vector<int> v; // create an empty vector
v.reserve (80); // reserve memory for 80 elements
//建立一個向量,并用默認的構造函數初始化,因此速度較慢
std::vector<T> v(5); // creates a vector and initializes it with five values
// (calls five times the default constructor of type T)
Table 6.2. Constructors and Destructors of Vectors Operation Effect
vector<Elem> c Creates an empty vector without any elements
vector<Elem> c1(c2) Creates a copy of another vector of the same type (all elements are copied)
vector<Elem> c(n) Creates a vector with n elements that are created by the default constructor
vector<Elem> c(n,elem) Creates a vector initialized with n copies of element elem
vector<Elem> c(beg,end) Creates a vector initialized with the elements of the range [beg,end)
c.~vector<Elem>() Destroys all elements and frees the memory
Table 6.3. Nonmodifying Operations of Vectors Operation Effect
c.size() Returns the actual number of elements
c.empty() Returns whether the container is empty (equivalent to size()==0, but might be faster)
c.max_size() Returns the maximum number of elements possible
capacity() Returns the maximum possible number of elements without reallocation
reserve() Enlarges capacity, if not enough yet[7] //如果不夠的話就繼續分配內存
c1 == c2 Returns whether c1 is equal to c2
c1 != c2 Returns whether c1 is not equal to c2 (equivalent to ! (c1==c2))
c1 < c2 Returns whether c1 is less than c2
c1 > c2 Returns whether c1 is greater than c2 (equivalent to c2<c1)
c1 <= c2 Returns whether c1 is less than or equal to c2 (equivalent to ! (c2<c1))
c1 >= c2 Returns whether c1 is greater than or equal to c2 (equivalent to ! (c1<c2))
Table 6.4. Assignment Operations of Vectors Operation Effect
c1 = c2 Assigns all elements of c2 to c1
c.assign(n,elem) Assigns n copies of element elem
c.assign(beg,end) Assigns the elements of the range [beg,end)
c1.swap(c2) Swaps the data of c1 and c2
swap(c1,c2) Same (as global function)
Table 6.5. Direct Element Access of Vectors Operation Effect
c.at(idx) Returns the element with index idx (throws range error exception if idx is out of range)
c[idx] Returns the element with index idx (no range checking)
c.front() Returns the first element (no check whether a first element exists)
c.back() Returns the last element (no check whether a last element exists)
通過at來訪問元素的時候如果越界會有一個out_of_range異常
用[]重載來訪問的時候只會報錯
Table 6.6. Iterator Operations of Vectors Operation Effect
c.begin() Returns a random access iterator for the first element
c.end() Returns a random access iterator for the position after the last element
c.rbegin() Returns a reverse iterator for the first element of a reverse iteration
c.rend() Returns a reverse iterator for the position after the last element of a reverse iteration
Table 6.7. Insert and Remove Operations of Vectors Operation Effect
c.insert(pos,elem) Inserts at iterator position pos a copy of elem and returns the position of the new element
c.insert(pos,n,elem) Inserts at iterator position pos n copies of elem (returns nothing)
c.insert(pos,beg,end) Inserts at iterator position pos a copy of all elements of the range [beg,end) (returns nothing)
c.push_back(elem) Appends a copy of elem at the end
c.pop_back() Removes the last element (does not return it)
c.erase(pos) Removes the element at iterator position pos and returns the position of the next element
c.erase(beg,end) Removes all elements of the range [beg,end) and returns the position of the next element
c.resize(num) Changes the number of elements to num (if size() grows, new elements are created by their default constructor)
c.resize(num,elem) Changes the number of elements to num (if size() grows, new elements are copies of elem)
c.clear() Removes all elements (makes the container empty)
std::vector<Elem> coll;
...
//remove all elements with value val
coll.erase(remove(coll.begin(),coll.end(),
val),
coll.end());
std::vector<Elem> coll;
...
//remove first element with value val
std::vector<Elem>::iterator pos;
pos = find(coll.begin(),coll.end(),
val);
if (pos != coll.end()) {
coll.erase(pos);
}
vector<bool>有特殊的函數
Table 6.8. Special Operations of vector<bool> Operation Effect
c.flip() Negates all Boolean elements (complement of all bits)
m[idx].flip() Negates the Boolean element with index idx (complement of a single bit)
m[idx] = val Assigns val to the Boolean element with index idx (assignment to a single bit)
m[idx1] = m[idx2] Assigns the value of the element with index idx2 to the element with index idx1
Examples of Using Vectors
The following example shows a simple usage of vectors:
// cont/vector1.cpp
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
//create empty vector for strings
vector<string> sentence;
//reserve memory for five elements to avoid reallocation
sentence.reserve(5);
//append some elements
sentence.push_back("Hello,");
sentence.push_back("how");
sentence.push_back("are");
sentence.push_back("you");
sentence.push_back("?");
//print elements separated with spaces
copy (sentence.begin(), sentence.end(),
ostream_iterator<string>(cout," "));
cout << endl;
//print ''technical data''
cout << " max_size(): " << sentence.max_size() << endl;
cout << " size(): " << sentence.size() << endl;
cout << " capacity(): " << sentence.capacity() << endl;
//swap second and fourth element
swap (sentence[1], sentence [3]);
//insert element "always" before element "?"
sentence.insert (find(sentence.begin(),sentence.end(),"?"),
"always");
//assign "!" to the last element
sentence.back() = "!";
//print elements separated with spaces
copy (sentence.begin(), sentence.end(),
ostream_iterator<string>(cout," "));
cout << endl;
//print "technical data" again
cout << " max_size(): " << sentence.max_size() << endl;
cout << " size(): " << sentence.size() << endl;
cout << " capacity(): " << sentence.capacity() << endl;
}
The output of the program might look like this:
Hello, how are you ?
max_size(): 268435455
size(): 5
capacity(): 5
Hello, you are how always !
max_size(): 268435455
size(): 6
capacity(): 10
文章來源:
http://ly-weiwei.blog.163.com/blog/static/7297528320092311263852