ETH官方钱包

創(chuàng)作內(nèi)容

6 GP

【進(jìn)度】3D背景—實(shí)裝影子2、cube shadow map 1

作者:Shark│2019-01-11 02:53:03│巴幣:20│人氣:1211
前一篇在此
巴哈姆特
官網(wǎng)

這篇嘗試一個(gè)做法,不同程式語(yǔ)言用不同底色。
C++的引擎本體:艾莉兒的顏色,粉紅底。
shader:要兩個(gè)電子妖精合作,用黃底
本篇沒出現(xiàn)鈷寶負(fù)責(zé)的輔助工具,以後如果有就是鈷寶的顏色:淺藍(lán)色。

同時(shí)發(fā)在官網(wǎng)
Blogspot可以修改表格邊框顏色,所以改用邊框顏色區(qū)分程式語(yǔ)言。



前一篇是Direct3D,這一篇是移植到Linux,要把shadow map用OpenGL再寫一份。

一代Windows和Linux都是用OpenGL,為何二代Windows版要改用D3D11?因?yàn)殡S著二代採(cǎi)用更進(jìn)階的OpenGL功能,發(fā)現(xiàn)Windows版的OpenGL驅(qū)動(dòng)程式通常寫得比較爛,像是效能比較差、同一個(gè)晶片有些功能D3D有但OpenGL沒有,可能OpenGL用的人比較少所以顯卡廠商就沒有認(rèn)真寫驅(qū)動(dòng)程式。而且Windows Phone和Windows on ARM只能使用D3D,以後開發(fā)這些平臺(tái)的程式遲早要碰D3D。

步驟大致如下
  • 建立畫shadow map用的framebuffer。
  • 新增一個(gè)sampler物件,用來(lái)讀取shadow map。
  • 修改計(jì)算矩陣的方式。
  • 變更c(diǎn)ull和depth test狀態(tài),D3D11是套用物件一次設(shè)定一組狀態(tài),OpenGL是呼叫函式一項(xiàng)一項(xiàng)設(shè)定。
    //設(shè)成只畫背面、開啟depth test
    glEnable(GL_CULL_FACE);
    glCullFace(GL_FRONT);
    glDepthMask(GL_TRUE);
    glEnable(GL_DEPTH_TEST);
  • 一個(gè)shader要使用兩張貼圖時(shí)要呼叫g(shù)lActiveTexture()變更目前的slot,這是筆者首次在一個(gè)shader裡用兩張貼圖。
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, shadowMapTexture);
    glActiveTexture(GL_TEXTURE0);
    //之後畫模型時(shí)將模型的貼圖放在slot 0
  • 畫模型要準(zhǔn)備三組shader:不畫影子的、畫shadow map的、用shadow map畫影子的。把以前寫的shader也重整一下。

再來(lái)把DirectX的shader語(yǔ)言:HLSL改寫成OpenGL的shader語(yǔ)言:GLSL。
這一步不難,HLSL和GLSL其實(shí)很多地方可以一對(duì)一對(duì)應(yīng)。
float → float
float4 → vec4
線性內(nèi)插  lerp() → mix()
很多函式都同名,如normalize、dot、clamp。

float4x3 → mat3x4
  矩陣的行數(shù)和列數(shù)寫法相反。
mul(float4, float4x3) → vec4*mat3x4
  向量與矩陣相乘HLSL必須用mul函式,GLSL可以用乘號(hào)。

兩者差別較大的地方是程式載入shader的方式。

第一點(diǎn)是HLSL沒有限定程式進(jìn)入點(diǎn)的名稱,是在編譯shader時(shí)指定,但是GLSL規(guī)定叫做main(),所以把多個(gè)shader寫在同一個(gè)檔案裡有名稱相衝的問題。
第二點(diǎn)是HLSL是先用SDK附的工具——fxc.exe將程式碼編譯成byte code(中間碼),再把byte code給顯卡驅(qū)動(dòng)程式,遊戲只要附帶byte code。但GLSL要把程式碼附在遊戲裡,執(zhí)行遊戲時(shí)叫驅(qū)動(dòng)程式即時(shí)編譯。
雖然OpenGL後來(lái)也開發(fā)出自己的byte code格式——SPIR-V,但目前還有很多顯卡沒支援。

這點(diǎn)我用自製的輔助工具型電子妖精——鈷寶解決。

鈷寶:好……。(開始包shader)

花點(diǎn)工夫做個(gè)工具,把編譯、打包、壓縮、以及遊戲本體載入shader自動(dòng)化,之後就輕鬆了。

————————

到此為止一切都很順利,沒想到又在一個(gè)地方卡關(guān):畫模型+影子時(shí)畫不出任何東西,但切換成不畫影子的模式就正常。

鈷寶:那個(gè)……,shader坐標(biāo)計(jì)算……有錯(cuò)。
艾莉兒:sampler物件也沒設(shè)對(duì)。

如前一篇所說(shuō),vertex shader要算出頂點(diǎn)在shadow map裡的位置,即shadow map framebuffer的螢?zāi)蛔鴺?biāo),再給pixel/fragment shader使用。
由於兩者對(duì)螢?zāi)蛔鴺?biāo)的定義不一樣,pixel shader裡把螢?zāi)蛔鴺?biāo)轉(zhuǎn)換到貼圖坐標(biāo)的算式不同。
//要先除以w
float3 shadowMapPos = IN.shadowMapPos.xyz/IN.shadowMapPos.w;
//此時(shí)xy=-1~1,轉(zhuǎn)換成0~1且y要反向
shadowMapPos.xy = shadowMapPos.xy*float2(0.5,-0.5) + 0.5;
Direct3D經(jīng)過矩陣變換後Z坐標(biāo)是0~1,可以直接跟shadow map裡的數(shù)值比較,只有xy需要-1~1到0~1的轉(zhuǎn)換。

//要先除以w
vec3 shadowMapPos = varShadowMapPos.xyz/varShadowMapPos.w;
//此時(shí)xyz=-1~1,轉(zhuǎn)換成0~1,跟D3D不同的是y不需要反向
shadowMapPos = shadowMapPos*0.5 + 0.5;
OpenGL經(jīng)過矩陣變換後Z坐標(biāo)是-1~1,xyz都要轉(zhuǎn)換到0~1的範(fàn)圍,我把這個(gè)算式寫錯(cuò)。
OpenGL的Y不需要反向是因?yàn)槲夜室庾屬N圖倒立,詳細(xì)原因看這篇:【程式】倒立的OpenGL貼圖坐標(biāo)

還有忘了先呼叫g(shù)lUseProgram()再設(shè)定uniform變數(shù)。glGetUniformLocation()有個(gè)programID參數(shù),用這個(gè)參數(shù)指定要操作的program,但是glUniformi()是用glUseProgram()指定要操作的program。
//programID代表OpenGL的program物件
glUseProgram(programID);
int location=glGetUniformLocation(programID, "shadowMapSampler");
glUniform1i(location, 1);


————————

但修正這些錯(cuò)誤後變成模型畫得出來(lái),看不到影子。

艾莉兒:想辦法把GPU內(nèi)部資料秀出來(lái)看看吧。

實(shí)驗(yàn)一,把shadow map的貼圖坐標(biāo)顯示出來(lái)。
在fragment shader裡這樣寫。
//Direct3D
float3 shadowMapPos=IN.shadowMapPos.xyz/IN.shadowMapPos.w;
shadowMapPos.xy = shadowMapPos.xy*float2(0.5,-0.5)+0.5;
return float4(shadowMapPos.xyz,1);

//OpenGL
vec3 shadowMapPos=varShadowMapPos.xyz/varShadowMapPos.w;
shadowMapPos = shadowMapPos*0.5+0.5;
gl_FragColor=vec4(shadowMapPos.xyz,1);
這樣顏色的R分量是X坐標(biāo),G是Y坐標(biāo),B是與光源的距離(非線性)。

(暫時(shí)到Windows開發(fā)機(jī)上工作)
艾莉兒:弄好了,D3D11是這樣。

(再回到Linux開發(fā)機(jī))
艾莉兒:OpenGL是這樣。

看起來(lái)一片藍(lán)+紫看不出什麼東西,但重點(diǎn)是兩張圖一樣,可見坐標(biāo)計(jì)算是對(duì)的。

鈷寶:shadow map呢?
實(shí)驗(yàn)二,顯示shadow map本身。
//Direct3D
float3 shadowMapPos=IN.shadowMapPos.xyz/IN.shadowMapPos.w;
shadowMapPos.xy = shadowMapPos.xy*float2(0.5,-0.5)+0.5;
float4 depth = shadowMap1.Sample(defaultSampler, shadowMapPos.xy);
return float4(depth.rrr,1);

//OpenGL
vec3 shadowMapPos=varShadowMapPos.xyz/varShadowMapPos.w;
shadowMapPos = shadowMapPos*0.5+0.5;
vec4 depth = texture(defaultSampler2, shadowMapPos.xy);
gl_FragColor=vec4(depth.rrr,1);

(跑到Windows)
艾莉兒:D3D11。

(再到Linux)
艾莉兒:OpenGL。

D3D可以正確顯示shadow map,但OpenGL是一片黑。

艾莉兒:主人等一下,有些要注意的地方有做對(duì)嗎?
艾莉兒:我看有哪些……,Z buffer通常都建立成renderbuffer,但想在shader讀取Z buffer的話,Z buffer要建立成texture。
艾莉兒:shader裡的sampler必須是sampler2D型態(tài),不能是sampler2DShadow型態(tài)。

鈷寶:還有……貼圖和sampler物件,bind不能弄錯(cuò)。
艾莉兒:還有畫之前有沒有用glClear(GL_DEPTH_BUFFER_BIT)清除Z buffer?

再看一下code,這些都有注意到了,為了做這個(gè)實(shí)驗(yàn)在shader裡宣告了第三個(gè)sampler物件,使用普通sampler讀取Z buffer而不是shadow sampler。
還有用glCheckFramebufferStatus()檢查framebuffer狀態(tài),確定只有Z buffer沒有color buffer的framebuffer物件是可以使用的,不會(huì)被系統(tǒng)擋下來(lái)。

另外發(fā)現(xiàn)一件怪事,在Linux看實(shí)驗(yàn)一的圖顏色會(huì)變這樣。

上圖是GIMP,用Korora預(yù)設(shè)的看圖軟體Gwenview也一樣,但用Firefox開圖檔的話顏色正常,且GIMP的顏色挑選器可以取得正確的顏色。
把GIMP的color management功能關(guān)閉顏色就對(duì)了,可見問題在color management,但是Gwenview要怎麼關(guān)閉color management?查了一下……,好像沒辦法,是寫死在程式裡。

發(fā)現(xiàn)一個(gè)很蠢的錯(cuò)誤,算shadow map的FOV時(shí)忘了把角度換算成弧度,所以投影計(jì)算錯(cuò)了,下面才是正確的shadow map坐標(biāo)和shadow map。


但是修正角度後OpenGL還是畫不出shadow map。

艾莉兒:主人,應(yīng)該有兩種可能,shadow map根本沒被畫出來(lái),或是shadow map有畫出來(lái)但shader讀不到。
鈷寶:找到這個(gè),要不要用?
  apitrace  https://apitrace.github.io/

特別找了一個(gè)D3D和OpenGL的debug工具——apitrace來(lái)用,確定Z pass這一步?jīng)]問題,shadow map有畫出來(lái),問題出在shader讀不到貼圖。
也試過另一個(gè)debug工具——RenderDoc,這個(gè)似乎比較有名,但我用的時(shí)候常常當(dāng)?shù)簦恢荝enderDoc本身的bug、Korora套件庫(kù)收錄的版本有問題、還是要debug的程式要做什麼特別處理。

艾莉兒、鈷寶,換個(gè)方法吧。

解數(shù)學(xué)難題的時(shí)候,常用方法是把問題簡(jiǎn)化看會(huì)得到什麼解,再增加變因一步步還原成原來(lái)的題目。決定先不要用整個(gè)引擎測(cè)試,寫個(gè)簡(jiǎn)單的程式,只做「設(shè)法讓Z buffer有變化→把Z buffer畫到另一個(gè)framebuffer上」,坐標(biāo)是程式直接給-1~1的螢?zāi)蛔鴺?biāo),把坐標(biāo)轉(zhuǎn)換也省了。

————————

總算查出原因,以上過程花了兩天。

//這樣寫會(huì)錯(cuò)
glGenSamplers(1, &samplerObj);
glSamplerParameteri(samplerObj, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(samplerObj, GL_TEXTURE_MIN_FILTER,
  GL_LINEAR_MIPMAP_NEAREST);
  ……
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);


//解法一
glGenSamplers(1, &samplerObj);
glSamplerParameteri(samplerObj, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(samplerObj, GL_TEXTURE_MIN_FILTER,
  GL_LINEAR_MIPMAP_NEAREST);
  ……
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);


//解法二
glGenSamplers(1, &samplerObj);
glSamplerParameteri(samplerObj, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(samplerObj, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  ……
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);

內(nèi)插方式如果設(shè)定成有mipmap的,如上面的GL_LINEAR_MIPMAP_NEAREST,貼圖物件必須把mipmap設(shè)定完整,shader才能正常讀取貼圖。
貼圖的GL_TEXTURE_MAX_LEVEL屬性預(yù)設(shè)值是1000,也就是必須把最大到最小的mipmap都準(zhǔn)備好才能使用貼圖。

解法一是把GL_TEXTURE_MAX_LEVEL設(shè)成0,告訴驅(qū)動(dòng)程式只有原始大小,沒有縮小的mipmap。
設(shè)成1代表有「原始大小+縮小成1/2」共兩層,依此類推。

解法二是把內(nèi)插方式設(shè)成GL_LINEAR,確實(shí)地叫OpenGL不要用mipmap。

這個(gè)錯(cuò)誤難找出來(lái)是因?yàn)橛胓lGetError()取得錯(cuò)誤碼也是傳回0,代表沒有error。

這裡反鋸齒設(shè)定用OpenGL 3.3新增的sampler物件功能,較早的版本是把反鋸齒設(shè)定視為貼圖的屬性,用glTexParameteri()設(shè)定。

——結(jié)論:產(chǎn)生貼圖之後一定要呼叫 glTexParameteri(target, GL_TEXTURE_MAX_LEVEL, level) 設(shè)定mipmap層數(shù),即使沒使用mipmap也一樣。

艾莉兒:快看,總算畫出來(lái)了。
跟前一篇不是同一個(gè)程式故看起來(lái)有些不同。


離鏡頭較近的部分投射不出影子(如Patchouli的腳附近),因?yàn)楣庠次恢迷O(shè)得很近,讓有些部分在shadow map外面,不過總之OpenGL的shadow map可以用了。



決定再做一個(gè)東西:參考這篇用cubemap實(shí)做shadow map。
https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows

其實(shí)目前只能在測(cè)試程式看到影子,還沒辦法在遊戲裡正確畫出影子。

前一篇進(jìn)度文提過,使用shadow map要注意shadow map在3D空間裡的位置,沒被shadow map涵蓋的物體就畫不出影子。本遊戲是點(diǎn)光源且因?yàn)楣庠次恢玫年P(guān)係,照射角度有點(diǎn)大,要建立好幾個(gè)shadow map才能涵蓋所有可視範(fàn)圍(如下面俯視圖),這樣C++建立物件,還有shader裡讀取shadow map很麻煩。


光源是點(diǎn)光源的時(shí)候,用cubemap實(shí)作shadow map是最不用煩惱位置的解法,可以涵蓋全方向,雖然Cyber Sprite 2光只朝一個(gè)方向照而不會(huì)用到四面八方,但現(xiàn)在做好的話以後做其他3D遊戲也用得著。

介紹一下cubemap的概念:這是一種特別格式的貼圖,正如其名有六個(gè)面,假想有個(gè)正方體以原點(diǎn)為中心,六個(gè)面分別是六張貼圖,可能來(lái)自圖檔或即時(shí)繪製產(chǎn)生。

使用左手坐標(biāo)系,上圖的-X、-Y、+Z面在背後。

讀取平面貼圖時(shí),貼圖坐標(biāo)要給兩個(gè)軸決定讀取哪個(gè)位置的像素,數(shù)值在0和1之間。
讀取cubemap則要給三個(gè)軸,數(shù)值範(fàn)圍不限,把貼圖坐標(biāo)視為3D空間裡的點(diǎn),將此點(diǎn)與原點(diǎn)連接會(huì)與正方體有一個(gè)交點(diǎn),讀取正方體表面此位置的像素。

它的應(yīng)用之一是環(huán)境貼圖(站在中央,往四面八方看到的景色),點(diǎn)光源是從中央往四周照射,也可以用cubemap記錄點(diǎn)與光源的距離。

————————

這還是我第一次用cubemap,而且是即時(shí)繪製,除了貼圖物件以外還要建立framebuffer物件。
保持冷靜,一步一步解題目,先看使用cubemap要準(zhǔn)備哪些東西。

艾莉兒:我查一下……,產(chǎn)生貼圖的時(shí)候ArraySize和MiscFlags屬性要特別設(shè)。
  其他屬性跟平面貼圖一樣,就寬高和格式之類的。

D3D11_TEXTURE2D_DESC td;
td.ArraySize = 6;
td.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;
td.Format = DXGI_FORMAT_R16_TYPELESS;
……
//之後用CreateTexture2D()建立ID3D11Texture2D物件
此外因?yàn)橐裠epth buffer當(dāng)成貼圖使用,F(xiàn)ormat必須設(shè)成TYPELESS而不是UNORM或FLOAT。
有測(cè)試過,本遊戲的3D場(chǎng)景不是很大,16 bit的depth buffer就夠用,不需要用到24或32 bit。

艾莉兒:然後,shader resource物件建一個(gè)就好了,但是depth stencil物件要建六個(gè)。
//要建立這些物件
ID3D11ShaderResourceView* srView;
ID3D11DepthStencilView* dsView[6];
ID3D11DepthStencilView** dsViewPtr=dsView;

//texture是已經(jīng)建好的ID3D11Texture2D物件

D3D11_SHADER_RESOURCE_VIEW_DESC srDesc;
ZeroMemory(&srDesc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));
srDesc.Format=DXGI_FORMAT_R16_UNORM;
srDesc.ViewDimension=D3D11_SRV_DIMENSION_TEXTURECUBE;
srDesc.TextureCube.MipLevels=1;
device->CreateShaderResourceView(texture, &srDesc, &srView);

D3D11_DEPTH_STENCIL_VIEW_DESC dsDesc;
ZeroMemory(&dsDesc, sizeof(D3D11_DEPTH_STENCIL_VIEW_DESC));
dsDesc.Format=DXGI_FORMAT_D16_UNORM;
dsDesc.ViewDimension=D3D11_DSV_DIMENSION_TEXTURE2DARRAY;
dsDesc.Texture2DArray.ArraySize=1;
//用迴圈建立六個(gè)depth stencil view
for(int i=0;i<6;i++){
  dsDesc.Texture2DArray.FirstArraySlice=i;
  device->CreateDepthStencilView(texture, &dsDesc, dsViewPtr);
  dsViewPtr++;
}

艾莉兒:再來(lái),更新cubemap的時(shí)候要把場(chǎng)景畫六次,一次畫一個(gè)面。
分別算六次旋轉(zhuǎn)矩陣,把鏡頭面對(duì)+X、-X、+Y……這六個(gè)方向,套用六個(gè)depth stencil view。


上面那篇教學(xué)使用geometry shader,畫一次場(chǎng)景就可以更新六個(gè)面,但本遊戲不使用geometry shader,要跑迴圈畫六次。
float rotMatix[9]; //暫存的旋轉(zhuǎn)矩陣

for(int i=0;i<6;i++){
  //套用depth stencil view
  context->OMSetRenderTargets(0, NULL, dsView[i]);
  context->ClearDepthStencilView(dsView[i], 0.0, DEPTH_CLEAR_VALUE,0);

  //計(jì)算旋轉(zhuǎn)矩陣,這一步還未完成
  oneCubeFace(rotMatix, this->rotTr, FACE_DIR[i],FACE_TOP_DIR[i]);

  //用uniform buffer把shadow map位置傳給shader
  PerSceneUniform* data=(PerSceneUniform*)mapUniformBuffer(
    sppl->perSceneUniform, sizeof(SP3DCamera));
  this->calcViewProj(rotMatix, &data->camera);
  unmapUniformBuffer(sppl->perSceneUniform);
  //畫出物件
  ListIter iter;
  for(iter=objList->begin();iter!=objList->end();iter=iter->next()){
    SP3DObjBase* obj=(SP3DObjBase*)iter->data;
    if(obj->flags & _3DOBJ_VISIBLE){
      obj->draw(SP3DObjBase::DRAWTYPE_ZPASS);
    }
  }
}
這裡有些是自訂的函式和常數(shù),不是D3D11定義的。
本遊戲的場(chǎng)景配置並不需要六個(gè)面都畫,或許可以用個(gè)flag設(shè)定哪些面不用畫,省略一些計(jì)算。
計(jì)算旋轉(zhuǎn)矩陣嘛,雖然之前已經(jīng)把3D的公式都研究出來(lái)但還是有點(diǎn)麻煩,先只畫一面看看(把for迴圈暫時(shí)拿掉)。

艾莉兒:更新好cubemap後,用cubemap畫圖的時(shí)候跟平面貼圖一樣,呼叫context->PSSetShaderResources()。

這個(gè)比較容易。
context->PSSetShaderResources(2,1, &srView);
第一參數(shù)是shader裡的slot,要跟shader裡旳宣告對(duì)應(yīng),0與1已經(jīng)用在模型本身的貼圖和平面shadow map,所以這裡用2。

再來(lái)修改shader,鈷寶,看一下要準(zhǔn)備什麼。

鈷寶:嗯……,要宣告一個(gè)TextureCube變數(shù)。

好,在宣告貼圖的地方加一行。
Texture2D<float4> texture1:register(t0);  //一般貼圖
Texture2D<float> shadowMap1:register(t1);  //平面shadow map
TextureCube<float> cubeShadowMap1:register(t2);  //cube shadow map
register(t2)就是對(duì)應(yīng)到PSSetShaderResources()裡旳2。

鈷寶:sampler……,不用加新的。
鈷寶:讀取貼圖嘛……,只要貼圖是TextureCube型態(tài),會(huì)自動(dòng)判斷。


上面說(shuō)過cubemap貼圖坐標(biāo)要給三個(gè)軸,讀取貼圖的函式「texture物件.SampleCmpLevelZero()」,只要貼圖是TextureCube型態(tài),就會(huì)使用貼圖坐標(biāo)是三維向量的版本,平面貼圖則是貼圖坐標(biāo)是二維。
不過Z buffer裡儲(chǔ)存的距離,cubemap和平面貼圖計(jì)算法不一樣,大概是這樣寫吧。
vertex shader
//float3 worldPos為頂點(diǎn)在世界坐標(biāo)系的位置
OUT.shadowMapPos.xyz=mul(float4(worldPos,1), shadowMap.viewMatrix);
float3 absValue=abs(OUT.shadowMapPos.xyz);
float maxComponent = max(absValue.x, max(absValue.y, absValue.z));
OUT.shadowMapPos.w = maxComponent*shadowMap.projCoef.z+
  shadowMap.projCoef.w;

//shadowMapPos.xyz=鏡頭坐標(biāo)系,w=要跟shadow map比較的值

pixel shader
//shadowMapPos是vertex shader算出來(lái),像素在shadow map裡的位置

//對(duì)照:讀取平面shadow map的方法
float3 shadowMapPos=shadowMapPos.xyz/shadowMapPos.w;
shadowMapPos.xy= shadowMapPos.xy*float2(0.5,-0.5) + 0.5;
  //SampleCmpLevelZero參數(shù)為(sampler, 貼圖坐標(biāo), 要跟shadow map比較的值)
float notInShadow=shadowMap1.SampleCmpLevelZero(
  shadowMapSampler, shadowMapPos.xy, shadowMapPos.z);

//cubemap要這樣寫
float3 absValue=abs(shadowMapPos.xyz);
float maxComponent = max(absValue.x, max(absValue.y, absValue.z));
float notInShadow=cubeShadowMap1.SampleCmpLevelZero(
  shadowMapSampler, shadowMapPos.xyz, shadowMapPos.w/maxComponent);
平面shadow map要把xyz除以w,但cube shadow map要把w除以xyz的最大值。

寫了一大堆而且沒有中途測(cè)試部分功能,也不知道這樣寫對(duì)不對(duì)。
好吧,艾莉兒、鈷寶,先試試看吧。

結(jié)果,只有二樓地板,還有天花板的一小部分有影子。


艾莉兒:果然沒那麼簡(jiǎn)單……。
不過物件都有成功建立,表示產(chǎn)生物件的參數(shù)有填對(duì)。

做到這裡覺得還是先研究一下cubemap的性質(zhì)比較好,不然一直是霧裡摸索,像是cubemap坐標(biāo)、2D貼圖坐標(biāo)、render target坐標(biāo)這三者如何對(duì)應(yīng),找了幾篇說(shuō)明都沒有寫得很清楚的。

(待續(xù),寫到這裡已經(jīng)很長(zhǎng)了,分成另一篇)
引用網(wǎng)址:http://www.jamesdambrosio.com/TrackBack.php?sn=4256997
All rights reserved. 版權(quán)所有,保留一切權(quán)利

相關(guān)創(chuàng)作

同標(biāo)籤作品搜尋:Cyber Sprite|遊戲製作|程式|3D|OpenGL

留言共 0 篇留言

我要留言提醒:您尚未登入,請(qǐng)先登入再留言

6喜歡★shark0r 可決定是否刪除您的留言,請(qǐng)勿發(fā)表違反站規(guī)文字。

前一篇:22週年站聚遊記... 後一篇:【進(jìn)度】3D背景—cub...


face基於日前微軟官方表示 Internet Explorer 不再支援新的網(wǎng)路標(biāo)準(zhǔn),可能無(wú)法使用新的應(yīng)用程式來(lái)呈現(xiàn)網(wǎng)站內(nèi)容,在瀏覽器支援度及網(wǎng)站安全性的雙重考量下,為了讓巴友們有更好的使用體驗(yàn),巴哈姆特即將於 2019年9月2日 停止支援 Internet Explorer 瀏覽器的頁(yè)面呈現(xiàn)和功能。
屆時(shí)建議您使用下述瀏覽器來(lái)瀏覽巴哈姆特:
。Google Chrome(推薦)
。Mozilla Firefox
。Microsoft Edge(Windows10以上的作業(yè)系統(tǒng)版本才可使用)

face我們了解您不想看到廣告的心情? 若您願(yuàn)意支持巴哈姆特永續(xù)經(jīng)營(yíng),請(qǐng)將 gamer.com.tw 加入廣告阻擋工具的白名單中,謝謝 !【教學(xué)】