文章来源: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