本例中我们使用的纹理如下:

Bezier曲线大家应该都很熟悉了,Bezier曲线由4个控制点定义,Bezier曲线的数学定义为:

其中,p0~p3定义了4个控制点,b0~b3为伯恩斯坦多项式的项,s的范围为0.0~1.0从而覆盖了曲线上的所有点,如图:

伯恩斯坦多项式的形式如下:

在3D世界中,我们需要用Bezier曲面来代替Bezier曲线,由Bezier曲线扩展到Bezier曲面是十分简单的,只需将4个控制点扩展为4*4控制网格即可,Bezier曲面的数学定义如下:

其中,p0~p15代表4*4控制网格上的16个控制点,b0~b3与Bezier曲线一样,代表伯恩斯坦多项式的项,s与t代表2个方向,范围为0.0~1.0从而覆盖了曲面上的所有点。

关于Bezier曲面的知识了解这些就够了,我们就可以使用它来渲染飘动的红旗了,但是在渲染时,我们还有一个问题需要考虑,就是法线的问题,该如何求曲面上的点的法线呢?

我们知道,导数描述了任一给定点处的曲线的斜率,下图描述了

及其导数的图形表示:

所以,我们只要使用Bezier曲线的导数形式就可以求出对于Bezier曲面上的任一给定点在s和t两个方向上的曲线的斜率,然后使用向量叉积就求得了法线,如图:

有许多求导数的方法,在这里我们使用最简单的形式,我们定义:

的导数形式为

。记住,常数的导数为0。

根据如上的定义,我们就可以得到伯恩斯坦多项式的项的导数的定义:

好了,至此我们已经了解了所有所需的知识,接下来,就是用代码实现的时刻了^-^!

首先我们定义x,z方向上顶点的数量:

1 const int VERTEX_ROW_COUNT = 20;

2 const int VERTEX_COL_COUNT = 20;

接着我们定义顶点结构:

1 struct BEZIER_VERTEX

2 {

3 float x, y, z; //顶点坐标4 float u, v; //纹理坐标5 float Bs0, Bs1, Bs2, Bs3; //关于s的Bernstein多项式的4项的值6 float Bt0, Bt1, Bt2, Bt3; //关于t的Bernstein多项式的4项的值7 float dBs0, dBs1, dBs2, dBs3; //关于s的Bernstein多项式的4项的导数的值8 float dBt0, dBt1, dBt2, dBt3; //关于t的Bernstein多项式的4项的导数的值9

10 static LPDIRECT3DVERTEXDECLARATION9 pVertexDecl;

11 };

其中,Bs0~Bs3定义某个顶点所对应在s方向上的伯恩斯坦多项式的项的值,而Bt0~Bt3则定义某个顶点所对应在t方向上的伯恩斯坦多项式的项的值。dBs0~dBs3则是s方向上相应的伯恩斯坦多项式的项的导数的值,而dBt0~dBt3则是t方向上相应的伯恩斯坦多项式的项的导数的值。

顶点声明定义如下,我们把数据当做纹理传入顶点着色器:

1 D3DVERTEXELEMENT9 BezierVertexElements[] =

2 {

3 { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },

4 { 0, 3*sizeof(float), D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },

5 { 0, 5*sizeof(float), D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 },

6 { 0, 9*sizeof(float), D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 2 },

7 { 0, 13*sizeof(float), D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 3 },

8 { 0, 17*sizeof(float), D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 4 },

9

10 D3DDECL_END(),

11 };

12

13 if ( FAILED(m_pD3DDevice->CreateVertexDeclaration(BezierVertexElements, &BEZIER_VERTEX::pVertexDecl)) )

14 {

15 return E_FAIL;

16 }

为了方便起见,我们创建了在x,z方向上范围在0.0~1.0的顶点数据,这样的话,我们就不需要在将顶点数据规范化到0.0~1.0后再进行计算,这只是一种偷懒的方法^-^。接着,我们填入了顶点数据以及索引数据:

1 if ( FAILED(m_pD3DDevice->CreateVertexBuffer(VERTEX_ROW_COUNT*VERTEX_COL_COUNT*sizeof(BEZIER_VERTEX),

2 D3DUSAGE_WRITEONLY,

3 0,

4 D3DPOOL_MANAGED,

5 &m_pFlagVB,

6 NULL)) )

7 {

8 return E_FAIL;

9 }

10

11 int nTriangleCount = (VERTEX_ROW_COUNT - 1) * (VERTEX_COL_COUNT - 1) * 2;

12 if ( FAILED(m_pD3DDevice->CreateIndexBuffer(nTriangleCount * 3 * sizeof(WORD),

13 D3DUSAGE_WRITEONLY,

14 D3DFMT_INDEX16,

15 D3DPOOL_MANAGED,

16 &m_pFlagIB,

17 NULL)) )

18 {

19 return E_FAIL;

20 }

21 //22

23 BEZIER_VERTEX *pVertices = NULL;

24 m_pFlagVB->Lock( 0, 0, reinterpret_cast(&pVertices), 0 );

25

26 WORD *pIndices = NULL;

27 m_pFlagIB->Lock( 0, 0, reinterpret_cast(&pIndices), 0 );

28

29 float fRowStride = 1.0f / (VERTEX_ROW_COUNT - 1);

30 float fColStride = 1.0f / (VERTEX_COL_COUNT - 1);

31 int nIndex = 0;

32 for ( int nRowIndex = 0; nRowIndex < VERTEX_ROW_COUNT; ++nRowIndex )

33 {

34 for ( int nColIndex = 0; nColIndex < VERTEX_COL_COUNT; ++nColIndex )

35 {

36 //写入顶点数据37 int nVertexIndex = VERTEX_COL_COUNT*nRowIndex + nColIndex;

38

39 pVertices[nVertexIndex].x = nColIndex * fColStride;

40 pVertices[nVertexIndex].y = 0.0f;

41 pVertices[nVertexIndex].z = nRowIndex * fRowStride;

42

43 pVertices[nVertexIndex].u = pVertices[nVertexIndex].x;

44 pVertices[nVertexIndex].v = pVertices[nVertexIndex].z;

45

46 float fS = pVertices[nVertexIndex].x;

47 float fT = pVertices[nVertexIndex].z;

48

49 pVertices[nVertexIndex].Bs0 = (1.0f - fS) * (1.0f - fS) * (1.0f - fS);

50 pVertices[nVertexIndex].Bs1 = 3.0f * fS * (1.0f - fS) * (1.0f - fS);

51 pVertices[nVertexIndex].Bs2 = 3.0f * fS * fS * (1.0f - fS);

52 pVertices[nVertexIndex].Bs3 = fS * fS * fS;

53

54 pVertices[nVertexIndex].Bt0 = (1.0f - fT) * (1.0f - fT) * (1.0f - fT);

55 pVertices[nVertexIndex].Bt1 = 3.0f * fT * (1.0f - fT) * (1.0f - fT);

56 pVertices[nVertexIndex].Bt2 = 3.0f * fT * fT * (1.0f - fT);

57 pVertices[nVertexIndex].Bt3 = fT * fT * fT;

58

59 pVertices[nVertexIndex].dBs0 = -3.0f * (1.0f - fS) * (1.0f - fS);

60 pVertices[nVertexIndex].dBs1 = 3.0f - (12.0f * fS) + (9.0f * fS * fS);

61 pVertices[nVertexIndex].dBs2 = (6.0f * fS) - (9.0f * fS * fS);

62 pVertices[nVertexIndex].dBs3 = 3.0f * fS * fS;

63

64 pVertices[nVertexIndex].dBt0 = -3.0f * (1.0f - fT) * (1.0f - fT);

65 pVertices[nVertexIndex].dBt1 = 3.0f - (12.0f * fT) + (9.0f * fT * fT);

66 pVertices[nVertexIndex].dBt2 = (6.0f * fT) - (9.0f * fT * fT);

67 pVertices[nVertexIndex].dBt3 = 3.0f * fT * fT;

68 //69

70 /*71 写入索引数据,索引格式:72 2 3,573 1,4 674 */

75 if ( nRowIndex != (VERTEX_ROW_COUNT-1) )

76 {

77 if ( nColIndex != (VERTEX_COL_COUNT-1) )

78 {

79 pIndices[nIndex++] = nRowIndex*VERTEX_COL_COUNT + nColIndex;

80 pIndices[nIndex++] = (nRowIndex+1)*VERTEX_COL_COUNT + nColIndex;

81 pIndices[nIndex++] = (nRowIndex+1)*VERTEX_COL_COUNT + nColIndex + 1;

82

83 pIndices[nIndex++] = nRowIndex*VERTEX_COL_COUNT + nColIndex;

84 pIndices[nIndex++] = (nRowIndex+1)*VERTEX_COL_COUNT + nColIndex + 1;

85 pIndices[nIndex++] = nRowIndex*VERTEX_COL_COUNT + nColIndex + 1;

86 }

87 }

88 }

89 }

90

91 m_pFlagIB->Unlock();

92 m_pFlagVB->Unlock();

93

94 return S_OK;

在顶点着色器中,首先我们定义数据:

1 vs.1.1

2

3 dcl_position v0

4 dcl_texcoord0 v1

5 dcl_texcoord1 v7

6 dcl_texcoord2 v8

7 dcl_texcoord3 v9

8 dcl_texcoord4 v10

9

10 // v7 - 关于s的Bernstein多项式的4项的值

11 // v8 - 关于t的Bernstein多项式的4项的值

12 mov r7, v7

13 mov r8, v8

14

15 // v9 - 关于s的用于求切线的导数的值

16 // v10 - 关于t的用于求切线的导数的值

17 mov r9, v9

18 mov r10, v10

接着,我们使用常量寄存器c10~c25表示4*4控制网格的16个控制点,我们计算在每个控制点的作用下,顶点的位置,然后与前一个控制点计算出的顶点的位置进行向量相加。同时,我们计算在每个控制点的作用下,两个方向上的斜率,然后再与前一个控制点计算出的两个方向上的斜率进行向量相加:

1 // c10 - s0t0控制点

2 mul r0.x, r7.x, r8.x

3 mul r1, c10, r0.x

4

5 mul r2, c10, r9.x

6 mul r3, c10, r10.x

7

8 // c11 - s0t1控制点

9 mul r0.x, r7.x, r8.y

10 mad r1, c11, r0.x, r1

11

12 mad r2, c11, r9.x, r2

13 mad r3, c11, r10.y, r3

14

15 // c12 - s0t2控制点

16 mul r0.x, r7.x, r8.z

17 mad r1, c12, r0.x, r1

18

19 mad r2, c12, r9.x, r2

20 mad r3, c12, r10.z, r3

21

22 // c13 - s0t3控制点

23 mul r0.x, r7.x, r8.w

24 mad r1, c13, r0.x, r1

25

26 mad r2, c13, r9.x, r2

27 mad r3, c13, r10.w, r3

28

29 // c14 - s1t0控制点

30 mul r0.x, r7.y, r8.x

31 mad r1, c14, r0.x, r1

32

33 mad r2, c14, r9.y, r2

34 mad r3, c14, r10.x, r3

35

36 // c15 - s1t1控制点

37 mul r0.x, r7.y, r8.y

38 mad r1, c15, r0.x, r1

39

40 mad r2, c15, r9.y, r2

41 mad r3, c15, r10.y, r3

42

43 // c16 - s1t2控制点

44 mul r0.x, r7.y, r8.z

45 mad r1, c16, r0.x, r1

46

47 mad r2, c16, r9.y, r2

48 mad r3, c16, r10.z, r3

49

50 // c17 - s1t3控制点

51 mul r0.x, r7.y, r8.w

52 mad r1, c17, r0.x, r1

53

54 mad r2, c17, r9.y, r2

55 mad r3, c17, r10.w, r3

56

57 // c18 - s2t0控制点

58 mul r0.x, r7.z, r8.x

59 mad r1, c18, r0.x, r1

60

61 mad r2, c18, r9.z, r2

62 mad r3, c18, r10.x, r3

63

64 // c19 - s2t1控制点

65 mul r0.x, r7.z, r8.y

66 mad r1, c19, r0.x, r1

67

68 mad r2, c19, r9.z, r2

69 mad r3, c19, r10.y, r3

70

71 // c20 - s2t2控制点

72 mul r0.x, r7.z, r8.z

73 mad r1, c20, r0.x, r1

74

75 mad r2, c20, r9.z, r2

76 mad r3, c20, r10.z, r3

77

78 // c21 - s2t3控制点

79 mul r0.x, r7.z, r8.w

80 mad r1, c21, r0.x, r1

81

82 mad r2, c21, r9.z, r2

83 mad r3, c21, r10.w, r3

84

85 // c22 - s3t0控制点

86 mul r0.x, r7.w, r8.x

87 mad r1, c22, r0.x, r1

88

89 mad r2, c22, r9.w, r2

90 mad r3, c22, r10.x, r3

91

92 // c23 - s3t1控制点

93 mul r0.x, r7.w, r8.y

94 mad r1, c23, r0.x, r1

95

96 mad r2, c23, r9.w, r2

97 mad r3, c23, r10.y, r3

98

99 // c24 - s3t2控制点

100 mul r0.x, r7.w, r8.z

101 mad r1, c24, r0.x, r1

102

103 mad r2, c24, r9.w, r2

104 mad r3, c24, r10.z, r3

105

106 // c25 - s3t3控制点

107 mul r0.x, r7.w, r8.w

108 mad r1, c25, r0.x, r1

109

110 mad r2, c25, r9.w, r2

111 mad r3, c25, r10.w, r3

最后,我们对最终的两个方向上的斜率进行叉积,计算法线,然后变换最后计算完毕的顶点坐标,同时传递纹理数据:

1 // 计算法线 = 2个方向上切线的叉积

2 mul r6, r3.yzxw, r2.zxyw

3 mad r6, -r2.yzxw, r3.zxyw, r6

4

5 // 单位化法线

6 dp3 r5.w, r6, r6

7 rsq r5.w, r5.w

8 mul r6, r6, r5.w

9

10 // c5 - 光的方向

11 dp3 oD0, r6, -c5

12

13 // c0 ~ c3 - 世界变换 + 视图变换 + 投影变换矩阵

14 dp4 oPos.x, r1, c0

15 dp4 oPos.y, r1, c1

16 dp4 oPos.z, r1, c2

17 dp4 oPos.w, r1, c3

18

19 mov oT0, v1

在应用程序中,我使用了一些三角函数来计算4*4控制网格的数据,大家可以随意修改看看不同的效果:

1 float fWrap1 = 2.0f * sin( GetTickCount()*0.001f );

2 float fWrap2 = 2.0f * cos( GetTickCount()*0.001f );

3

4 D3DXVECTOR4 v4ControlS0T0( 0.0f, 0.0f, 0.0f, 1.0f );

5 D3DXVECTOR4 v4ControlS0T1( 0.0f, 0.0f, 0.33f, 1.0f );

6 D3DXVECTOR4 v4ControlS0T2( 0.0f, 0.0f, 0.66f, 1.0f );

7 D3DXVECTOR4 v4ControlS0T3( 0.0f, 0.0f, 1.0f, 1.0f );

8

9 D3DXVECTOR4 v4ControlS1T0( 0.33f, 0.0f, 0.0f, 1.0f );

10 D3DXVECTOR4 v4ControlS1T1( 0.33f, 0.15f*fWrap1 + 0.15f*fWrap2, 0.33f, 1.0f );

11 D3DXVECTOR4 v4ControlS1T2( 0.33f, -0.15f*fWrap1 + 0.33f*fWrap2, 0.66f, 1.0f );

12 D3DXVECTOR4 v4ControlS1T3( 0.33f, 0.0f, 1.0f, 1.0f );

13

14 D3DXVECTOR4 v4ControlS2T0( 0.66f, 0.0f, 0.0f, 1.0f );

15 D3DXVECTOR4 v4ControlS2T1( 0.66f, 0.33f*fWrap1 + 0.15f*fWrap2, 0.33f, 1.0f );

16 D3DXVECTOR4 v4ControlS2T2( 0.66f, -0.33f*fWrap1 + 0.33f*fWrap2, 0.66f, 1.0f );

17 D3DXVECTOR4 v4ControlS2T3( 0.66f, 0.0f, 1.0f, 1.0f );

18

19 D3DXVECTOR4 v4ControlS3T0( 1.0f, 0.25f*fWrap2, 0.0f, 1.0f );

20 D3DXVECTOR4 v4ControlS3T1( 1.0f, 0.33f*fWrap1 + 0.33f*fWrap2, 0.33f, 1.0f );

21 D3DXVECTOR4 v4ControlS3T2( 1.0f, -0.33f*fWrap1 + 0.33f*fWrap2, 0.66f, 1.0f );

22 D3DXVECTOR4 v4ControlS3T3( 1.0f, 0.25f*fWrap2, 1.0f, 1.0f );

23

24 m_pD3DDevice->SetVertexShaderConstantF( 10, v4ControlS0T0, 1 );

25 m_pD3DDevice->SetVertexShaderConstantF( 11, v4ControlS0T1, 1 );

26 m_pD3DDevice->SetVertexShaderConstantF( 12, v4ControlS0T2, 1 );

27 m_pD3DDevice->SetVertexShaderConstantF( 13, v4ControlS0T3, 1 );

28 m_pD3DDevice->SetVertexShaderConstantF( 14, v4ControlS1T0, 1 );

29 m_pD3DDevice->SetVertexShaderConstantF( 15, v4ControlS1T1, 1 );

30 m_pD3DDevice->SetVertexShaderConstantF( 16, v4ControlS1T2, 1 );

31 m_pD3DDevice->SetVertexShaderConstantF( 17, v4ControlS1T3, 1 );

32 m_pD3DDevice->SetVertexShaderConstantF( 18, v4ControlS2T0, 1 );

33 m_pD3DDevice->SetVertexShaderConstantF( 19, v4ControlS2T1, 1 );

34 m_pD3DDevice->SetVertexShaderConstantF( 20, v4ControlS2T2, 1 );

35 m_pD3DDevice->SetVertexShaderConstantF( 21, v4ControlS2T3, 1 );

36 m_pD3DDevice->SetVertexShaderConstantF( 22, v4ControlS3T0, 1 );

37 m_pD3DDevice->SetVertexShaderConstantF( 23, v4ControlS3T1, 1 );

38 m_pD3DDevice->SetVertexShaderConstantF( 24, v4ControlS3T2, 1 );

39 m_pD3DDevice->SetVertexShaderConstantF( 25, v4ControlS3T3, 1 );

好了,让我们来欣赏我们的成果吧^-^:

java3d bezier曲面_使用Bezier曲面渲染飘动的红旗相关推荐

  1. opengl双三次bezier曲面_双三次Bezier曲面

    实验六 双三次 Bezier 曲面 一.实验目的 根据 Bizer 曲面的基础知识和数学基础,对其算法进行程序设计,验证算法的正确性, 并通过程序结果加深对常用曲面数学模型的理解. 二.实验任务( 2 ...

  2. catia 桥接曲面圆角_4.3.7.1-Catia曲面之多桥接曲面_简单构面

    本节内容主要介绍了Catia曲面之多桥接曲面_简单构面,视频时长7分59秒.桥接曲面是对一些连续的曲面创建过渡曲面使其缝合,其命令操作非常简单但是实际应用中比较难尤其是其支持面的选择,因此学习时要掌握 ...

  3. 样条曲面_这样的曲面是如何画成的,用好剪裁工具,便迎刃而解

    此图用SolidWorks2019版建模,用KeyShot 9.0 渲染 .SW原文件在今日网盘.(包含STEP格式) 建模过程: 1.在前视基准面上画圆,直径:20 . 2.曲面拉伸,长度:250 ...

  4. 样条曲面_用SolidWorks曲面制作一个:台灯底座造型

    此图用SolidWorks2019版建模,用KeyShot 9.0 渲染(上面两张图) .SW原文件已经分享,在今日文件夹里. 建模过程: 1.在前视基准面画中心线和半圆. 2.曲面旋转:180度,反 ...

  5. catia圆型与矩形的桥接_4.3.7.2-桥接曲面_复杂构面

    本节内容主要介绍了Catia桥接曲面_复杂构面,视频时长11分39秒.接上节所讲,本节主要通过天圆地方这个案例来讲解了其应用该命令和多截面曲面具有较多的相似之处学习时可比较两命令,对于两曲线也可利用桥 ...

  6. catia曲面扫掠命令详解_扫掠曲面在CATIA曲面造型中的应用

    CATIA 扫描的详细解法 直纹面(Line Sweep ),系统自动以直线作为轮廓形状,只需要指定导引线及相关的边界条件,也就是将直线沿着导引线为轨迹进行扫掠,形成直纹面: 圆弧曲面(Circle ...

  7. matlab求第二类曲面积分,第二型曲面积分的参数形式计算

    给出"第二型曲面积分"的一种计算方法,即在曲面的参数形式下直接将曲面积分转化成参数区域上的一个二重积分,由此可使"第二型曲面积分"的计算问题得到简化.此法是对菲 ...

  8. java画bezier曲面_计算机图形学上机实验4-实现Bezier曲线和Bezier曲面的绘制

    <计算机图形学上机实验4-实现Bezier曲线和Bezier曲面的绘制>由会员分享,可在线阅读,更多相关<计算机图形学上机实验4-实现Bezier曲线和Bezier曲面的绘制(9页珍 ...

  9. opengl双三次bezier曲面_试驾艾瑞泽5 PLUS:双外观设计,搭L2级全速域驾驶辅助,月销要破万?...

    转载请注明出处,关注我们一起侃车! "硬糖少女303"可能35岁以上就很少有人了解,但在18至28岁左右会有很多人为之疯狂.艾瑞泽5 PLUS选择这样一个年轻组合为其代言,可以很清 ...

最新文章

  1. Fedora 20 安装搜狗拼音输入法
  2. javaWeb防止恶意登陆或防盗链的使用
  3. java 热替换 匿名类_Java 类的热替换
  4. 微信支付异常:appid and openid not match
  5. DPDK uio驱动实现(二十)
  6. 对计算机网络的父亲,父亲节朋友圈对老爸说的话 写给父亲催泪的话简短
  7. P1341 无序字母对
  8. maven项目发布到tomcat里lib包没有发布的问题
  9. php如何控制用户对图片的访问 PHP禁止图片盗链
  10. 路由算法之LS算法和DV算法全面分析
  11. svn diff和vimdiff的使用
  12. java inflater_Android下LayoutInflater的正确使用姿势
  13. 有道词典 PC端 手机端 单词 背 个数 不同步 解决
  14. jlink接口定义(jlink接口定义 swd4线)
  15. 变废为宝,用旧电脑自己DIY组建 NAS 服务器
  16. 迈卡名车茂深度访谈信达名车——宋丙刚:自信坚韧 坚持不懈
  17. SDCC和Keil之stc89c52资料(纪念51单片机40周年)
  18. vb.net产生随机数
  19. 苹果CMS个人收款扫码收款插件 闪电收款
  20. php 替换 全角符号,php如何实现全角符号转半角

热门文章

  1. d.ts你知道多少?
  2. 一题多解——求数组中每个元素出现的次数
  3. 四元数转yaw、pitch、roll
  4. 米尔科技ZYNQ-7z020学习(1)
  5. USB OTG 检测原理
  6. 我在北京,我不在北京
  7. 前端移动端superslide插件(使用方法)
  8. AI并非源自现代技术?最早的算法可追溯到三千年前
  9. c语言酶切算法,科学网—FitHiC V1算法解析(一) - 卢锐的博文
  10. Linux安装jdk(超级简单)