国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

3D游戲引擎系列十二

2019-11-09 14:31:14
字體:
來源:轉載
供稿:網友

筆者介紹:姜雪偉,IT公司技術合伙人,IT高級講師,CSDN社區專家,特邀編輯,暢銷書作者,國家專利發明人;已出版書籍:《手把手教你架構3D游戲引擎》電子工業出版社和《Unity3D實戰核心技術詳解》電子工業出版社等。

CSDN課程視頻網址:http://edu.csdn.net/lecturer/144

對于3D游戲產品都需要陰影技術的實現,陰影的運行效率也成為判定游戲研發技術水平的手段之一。游戲中實現陰影的方式有很多種,主要分三種:一種是對于靜態物體比如建筑物可以使用LightMap渲染,將建筑的陰影直接渲染到地面上這種技術廣泛應用在移動端,Unity引擎本身就提供了此功能。另一種是對于游戲中動態的物體,實現方式是在移動端或者在網頁游戲中為了優化效率,直接用一張帶有Alpha通道的貼圖放到角色的下面,可以實時跟隨角色移動。第三種實現方式是該書重點講解的實時陰影渲染,實時陰影在PC端游特別是次時代網游中很常見,鑒于PC端硬件的強大處理能力,應用實時陰影技術對整個游戲場景進行渲染,為的是增加游戲場景的真實性。當然實時陰影技術的運用會對CPU和GPU有一定的消耗,所以對于實時陰影的渲染,可以通過摒棄掉不需要實時渲染的建筑物進行效率優化。實時渲染技術常用的是PSSM(Parallel-Split ShadowMap)算法,實現陰影的算法非常多的,我就不一一列舉了。PSSM通過字面意思知道就是平行切分視錐,游戲中實時陰影的渲染效果如下圖:

要實現如此的效果,得從PSSM實現的原理講起,PSSM算法的核心就是把視椎體進行分割,然后分別渲染組合。語言講解不如看圖直觀,先通過視錐體分割說起。效果如下圖:

PSSM實時陰影的繪制首先需要燈光,在現實生活中,白天只有太陽出來了才可以看到影子。在虛擬世界中也是一樣的,場景使用的是Directional(平行光)相當于現實世界的太陽光。上圖左邊部分顯示的是視景體的投影,利用PSSM算法將其平行的分割成多個部分,然后對每個部分進行渲染,分割成的塊數是可以自己設置的。右半部分是頂視角觀看的分割效果,把物體分成三塊進行實時陰影的渲染。渲染的計算是GPU中執行的,在GPU中執行的流程如下圖:

上圖的處理流程首先是場景中的燈光照射到需要投影的物體上,接下來程序對投影的物體頂點進行矩陣變換將其轉換到投影空間中,再轉換到裁剪空間進行視口的平行分割,最后將其分別渲染出來。渲染陰影流程講完了接下來解決Shader渲染的問題,我們把平行分割的計算放到GPU中執行,需要編寫Shader腳本文件,新建一個文本文件把其擴展名字改成.fx。Shader的完整內容如下:

float4x4 g_mViewPRoj;void VS_RenderShadowMap(  float4 vPos : POSITION,  out float4 vPosOut : POSITION,  out float3 vPixelOut : TEXCOORD0){  // pass vertex position through as usual  vPosOut = mul(vPos, g_mViewProj);  // output pixel pos  vPixelOut=vPosOut.xyz;}float4 PS_RenderShadowMap(float3 vPixelPos : TEXCOORD0): COLOR{  // write z coordinate (linearized depth) to texture  return vPixelPos.z;}// This technique is used when rendering meshes to the shadowmap// technique RenderShadowMap{  pass p0  {    // render back faces to hide artifacts    CullMode = CW;    VertexShader = compile vs_2_0 VS_RenderShadowMap();    PixelShader = compile ps_2_0 PS_RenderShadowMap();  }}float3 g_vLightDir;float3 g_vLightColor;float3 g_vAmbient;float g_fShadowMapSize;float4x4 g_mShadowMap;// no filtering in floating point texturesampler2D g_samShadowMap  =sampler_state{  MinFilter = Point;  MagFilter = Point;  MipFilter = None;  AddressU = Border;  AddressV = Border;  BorderColor = 0xFFFFFFFF;};void VS_Shadowed(  in float4 vPos : POSITION,  in float3 vNormal : NORMAL,  in float fAmbientIn : TEXCOORD0,  out float4 vPosOut : POSITION,  out float4 vShadowTex : TEXCOORD0,  out float fAmbientOut : TEXCOORD1,  out float3 vDiffuse : COLOR0){  // pass vertex position through as usual  vPosOut = mul(vPos, g_mViewProj);  // calculate per vertex lighting  vDiffuse = g_vLightColor * saturate(dot(-g_vLightDir, vNormal));  // coordinates for shadowmap  vShadowTex = mul(vPos, g_mShadowMap);  // ambient occlusion  fAmbientOut = saturate(0.5f+fAmbientIn);}float4 PS_Shadowed(  float4 vShadowTex : TEXCOORD0,  float fAmbientOcclusion : TEXCOORD1,  float4 vDiffuse : COLOR0) : COLOR{  float fTexelSize=1.0f/g_fShadowMapSize;  // project texture coordinates  vShadowTex.xy/=vShadowTex.w;  // 2x2 PCF Filtering  //   float fShadow[4];  fShadow[0] = (vShadowTex.z < tex2D(g_samShadowMap, vShadowTex).r);  fShadow[1] = (vShadowTex.z < tex2D(g_samShadowMap, vShadowTex + float2(fTexelSize,0)).r);  fShadow[2] = (vShadowTex.z < tex2D(g_samShadowMap, vShadowTex + float2(0,fTexelSize)).r);  fShadow[3] = (vShadowTex.z < tex2D(g_samShadowMap, vShadowTex + float2(fTexelSize,fTexelSize)).r);  float2 vLerpFactor = frac(g_fShadowMapSize * vShadowTex);  float fLightingFactor = lerp(lerp( fShadow[0], fShadow[1], vLerpFactor.x ),                               lerp( fShadow[2], fShadow[3], vLerpFactor.x ),                               vLerpFactor.y);  // multiply diffuse with shadowmap lookup value  vDiffuse*=fLightingFactor;  // final color  float4 vColor=1;  vColor.rgb = saturate(g_vAmbient*fAmbientOcclusion + vDiffuse).rgb;  return vColor;}// This technique is used to render the final shadowed meshes//technique Shadowed{  pass p0  {    /	/ render front faces	CullMode = CCW;	  VertexShader = compile vs_2_0 VS_Shadowed();	  PixelShader = compile ps_2_0 PS_Shadowed();  }}	理論講了很多,Shader代碼實現起來比較簡單,為了消除陰影鋸齒,使用了PCF Filtering過濾技術。其他的代碼跟以前講的很類似這里就不一一分析了。接下來通過C++函數接口將參數傳遞給Shader文件,C++代碼核心函數實現如下所示:

void RenderScene(D3DXMATRIX &mView, D3DXMATRIX &mProj){  // Set constants  //  D3DXMATRIX mViewProj=mView * mProj;  _pEffect->SetMatrix("g_mViewProj",&mViewProj);  _pEffect->SetVector("g_vLightDir",&_vLightDir);  _pEffect->SetVector("g_vLightColor",&_vLightDiffuse);  _pEffect->SetVector("g_vAmbient",&_vLightAmbient);  _pEffect->SetFloat("g_fShadowMapSize",(FLOAT)_iShadowMapSize);  // enable effect  unsigned int iPasses=0;  _pEffect->Begin(&iPasses,0);  // for each pass in effect   for(unsigned int i=0;i<iPasses;i++)  {    // start pass    _pEffect->BeginPass(i);    {      // for each subset in mesh      for(DWord j=0;j<_iMeshMaterials;j++)      {        // draw subset        _pMesh->DrawSubset(j);      }    }    // end pass    _pEffect->EndPass();  }  // disable effect  _pEffect->End();}	該函數主要是將Shader文件中需要使用的參數通過C++代碼傳遞給GPU進行渲染,在介紹PSSM原理時對物體進行Split操作。在C++中的函數如下所示:

void CalculateSplitDistances(void){  // Reallocate array in case the split count has changed  //  delete[] _pSplitDistances;  _pSplitDistances=new float[_iNumSplits+1];  _fSplitSchemeLambda=Clamp(_fSplitSchemeLambda,0.0f,1.0f);  for(int i=0;i<_iNumSplits;i++)  {    float fIDM=i/(float)_iNumSplits;    float fLog=_fCameraNear*powf((_fCameraFar/_fCameraNear),fIDM);    float fUniform=_fCameraNear+(_fCameraFar-_fCameraNear)*fIDM;    _pSplitDistances[i]=fLog*_fSplitSchemeLambda+fUniform*(1-_fSplitSchemeLambda);  }  // make sure border values are right  _pSplitDistances[0]=_fCameraNear;  _pSplitDistances[_iNumSplits]=_fCameraFar;}最后將上述實現的兩個關鍵函數在Render函數中調用,完成最終的代碼實現。渲染函數如下所示:
void Render(void){  // move camera, adjust settings, etc..  DoControls();  // calculate the light position  _vLightSource=D3DXVECTOR3(-200*sinf(_fLightRotation),120,200*cosf(_fLightRotation));  _vLightTarget=D3DXVECTOR3(0,0,0);  // and direction  _vLightDir=D3DXVECTOR4(_vLightTarget-_vLightSource,0);  D3DXVec4Normalize(&_vLightDir,&_vLightDir);  // calculate camera aspect  D3DPRESENT_PARAMETERS pp=GetApp()->GetPresentParams();  float fCameraAspect=pp.BackBufferWidth/(float)pp.BackBufferHeight;  AdjustCameraPlanes();  CalculateSplitDistances();  // Clear the screen  //  GetApp()->GetDevice()->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DXCOLOR(0.5f,0.5f,0.5f,0.5f), 1.0f, 0);for(int iSplit=0;iSplit<_iNumSplits;iSplit++)  {    // use numpad to skip rendering    if(GetKeyDown(VK_NUMPAD1+iSplit)) continue;    // near and far planes for current frustum split    float fNear=_pSplitDistances[iSplit];    float fFar=_pSplitDistances[iSplit+1];    // Calculate corner points of frustum split    float fScale=1.1f;    D3DXVECTOR3 pCorners[8];    CalculateFrustumCorners(pCorners,_vCameraSource,_vCameraTarget,_vCameraUpVector,                            fNear,fFar,_fCameraFOV,fCameraAspect,fScale);    // Calculate view and projection matrices    CalculateLightForFrustum(pCorners);    // Enable rendering to shadowmap    _ShadowMapTexture.EnableRendering();    // Clear the shadowmap    GetApp()->GetDevice()->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, 0xFFFFFFFF, 1.0f, 0);    // Set up shaders    // To hide artifacts, only render back faces of the scene    _pEffect->SetTechnique("RenderShadowMap");    // Render the scene to the shadowmap    RenderScene(_mLightView,_mLightProj);    // Go back to normal rendering    _ShadowMapTexture.DisableRendering();    /////////////////////////////////////////////////////////////    // At this point we have the shadowmap texture rendered.   //    /////////////////////////////////////////////////////////////    // Calculate a matrix to transform points to shadowmap texture coordinates    // (this should be exactly like in your standard shadowmap implementation)    float fTexOffset=0.5f+(0.5f/(float)_iShadowMapSize);    D3DXMATRIX mTexScale(   0.5f,               0.0f,      0.0f,   0.0f,                            0.0f,              -0.5f,      0.0f,   0.0f,                            0.0f,               0.0f,      1.0f,   0.0f,                            fTexOffset,    fTexOffset,     0.0f,   1.0f );    D3DXMATRIX mShadowMap=_mLightView * _mLightProj * mTexScale;    // store it to the shader    _pEffect->SetMatrix("g_mShadowMap",&mShadowMap);    // Since the near and far planes are different for each    // rendered split, we need to change the depth value range    // to avoid rendering over previous splits    D3DVIEWPORT9 CameraViewport;    GetApp()->GetDevice()->GetViewport(&CameraViewport);    // as long as ranges are in order and don't overlap it should be all good...    CameraViewport.MinZ=iSplit/(float)_iNumSplits;    CameraViewport.MaxZ=(iSplit+1)/(float)_iNumSplits;    GetApp()->GetDevice()->SetViewport(&CameraViewport);    // use the current splits near and far plane    // when calculating matrices for the camera    CalculateViewProj(_mCameraView, _mCameraProj,                      _vCameraSource,_vCameraTarget,_vCameraUpVector,                      _fCameraFOV, fNear, fFar, fCameraAspect);    // setup shaders    _pEffect->SetTechnique("Shadowed");    // bind shadowmap as a texture    GetApp()->GetDevice()->SetTexture(0,_ShadowMapTexture.GetColorTexture());    // render the final scene    RenderScene(_mCameraView, _mCameraProj);    // unbind texture so we can render on it again    GetApp()->GetDevice()->SetTexture(0,NULL);    // draw the shadowmap texture to HUD    RenderSplitOnHUD(iSplit);  }  // render other HUD stuff  RenderHUD();}整個PSSM的核心代碼就實現完成了,最后本書實現了9級平行分割對物體陰影的實現,實現效果如下:


上一篇:Lua - 20

下一篇:二維碼生成

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 林周县| 霞浦县| 嘉荫县| 丹江口市| 青铜峡市| 广宁县| 灌南县| 正宁县| 嘉兴市| 永嘉县| 长治市| 潢川县| 庆城县| 合川市| 铅山县| 奉贤区| 唐山市| 安龙县| 都昌县| 中西区| 崇仁县| 林周县| 班戈县| 嘉定区| 基隆市| 东宁县| 九台市| 湖南省| 新干县| 海盐县| 凌源市| 文水县| 时尚| 沙雅县| 拉萨市| 台南县| 平陆县| 怀集县| 怀集县| 南陵县| 丰镇市|