zoukankan      html  css  js  c++  java
  • 使用Bezier曲面渲染飘动的红旗

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

    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<void**>(&pVertices), 0 );
    25
    26 WORD *pIndices = NULL;
    27 m_pFlagIB->Lock( 0, 0, reinterpret_cast<void**>(&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,5
    73 1,4 6
    74       */
    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 );

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

  • 相关阅读:
    重构机房收费系统总结1之配置文件+反射+抽象工厂
    win7 下jenkins配置与使用
    ant-jmeter批量脚本
    ant批量运行Jmeter脚本遇到 Content is not allowed in prolog.问题及解决方案
    JMeter学习笔记--JMeter前置处理器
    JMeter学习笔记--详解JMeter定时器
    JMeter学习笔记--详解JMeter配置元件
    JMeter学习笔记--详解JMeter逻辑控制器
    JMeter学习笔记--JMeter监听器
    JMeter学习笔记--创建数据库测试计划
  • 原文地址:https://www.cnblogs.com/twjcnblog/p/2245295.html
Copyright © 2011-2022 走看看