This is because I wanted more fine control over the individual textures that a cube map was giving me, I could possibly change it to a cube map ...
Downloads
Documentation
Community
Showcase
About
Viewmatrixforpointlightshadows
Graphics
Richard_Stern
December30,2020,4:07am
#1
Hiall.
I’vebeenworkingonpointlightshadowsandhavethemalmostworking,somethingjustseemstobeabitoff.I’mnotusingacubemap,insteadI’musinganarrayof6textures.ThisisbecauseIwantedmorefinecontrolovertheindividualtexturesthatacubemapwasgivingme,IcouldpossiblychangeittoacubemapbutIdon’tthinkthat’stheissueI’mhaving.
Whenthepointlightisrightoverthescene,everythinglooksgreat,butasImovethelightofftotheright,thingsstartstogowrong:
Shadows1435×80492.8KB
Therightmostspherelooksgreat,buttheleftonesarejustdark.There’snothingcastingthatshadow.
Iwrotesomedebugcodetopaintthepixelsdifferentcolorsbasedonwhichfaceofmyshadowmaparrayisbeingusedandsureenough:
debugShadows1435×80453.6KB
Thebuggyareaisallinyellowwhichcorrespondstotheleftfaceofmyarray/cube.Thatmakessense,itistotheleftofthelight.
Sotheproblemmustbeintherenderingofthatface.Perhapstheviewmatrix?
Thisisthecodebeingusedtogeneratetheviewmatrix:
switch(eFace)
{
caseCubeMapFace.PositiveX: returnMatrix.CreateLookAt(v3Pos,v3Pos+Vector3.Right,Vector3.Up);
caseCubeMapFace.NegativeX: returnMatrix.CreateLookAt(v3Pos,v3Pos+Vector3.Left,Vector3.Up);
caseCubeMapFace.PositiveY: returnMatrix.CreateLookAt(v3Pos,v3Pos+Vector3.Up,Vector3.Backward);
caseCubeMapFace.NegativeY: returnMatrix.CreateLookAt(v3Pos,v3Pos+Vector3.Down,Vector3.Forward);
caseCubeMapFace.PositiveZ: returnMatrix.CreateLookAt(v3Pos,v3Pos+Vector3.Backward,Vector3.Down);
caseCubeMapFace.NegativeZ: returnMatrix.CreateLookAt(v3Pos,v3Pos+Vector3.Forward,Vector3.Up);
default: returnMatrix.Identity;
}
Itriedmessingwiththeupvectorincaseitwasupsidedownorsidewaysbutthatdidn’timprovethings.
Itdoesn’tseemliketheproblemcouldbeanyotherpartofthepipelinesinceit’sworkingcorrectlyfortherightsphereandcorrectlyidentifyingwhichfaceoftheshadowmaptouse,butjustincase,here’stherestofthecode:
//C#codeusedtodrawtheshadowmap
privatevoidDrawOmnidirectionalShadowMap(Lightlight,intnLightIndex)
{
//Sixfacesofthecubemap
for(inti=0;iclosestDirection)
{
closestDirection=result;
faceIndex=face;
}
}
float4lightingPosition=mul(float4(worldPosition,1),_PointLightSpaceMatrix[faceIndex]);
float2shadowTexCoord=mad(0.5,lightingPosition.xy/lightingPosition.w,float2(0.5,0.5));
shadowTexCoord.y=1.0f-shadowTexCoord.y;
floatourDepth=(lightingPosition.z/lightingPosition.w);
shadowContribution=CalcPointShadowsPCF(ourDepth,NdotL,float3(shadowTexCoord,i*CubeSides+faceIndex));
//Dorestoflightingandrendering
Anyideawhythatfaceisn’tworking?
Richard_Stern
December30,2020,4:34am
#2
IusedSpriteBatchtorenderouttheleftfaceofthecubeanditlooksfine:
ShadowRender603×5246.86KB
SoIguesstheproblemisn’ttheviewmatrix.Anyonehaveanyideas?
markus
December30,2020,10:18am
#3
Justawildguess,buttheimagecouldbesampledupsidedown.Yourclearcolorisblack,whichwouldmakeeverythingshadowed.Maybetryclearingtowhite,andseeiftheshadowisgone.
Iwouldsuggesttouseacubemapinsteadofthe6separatetextures.Whensamplingfromacubemap,youjustpassinadirection,andthehardwarefiguresoutwhichfacetosamplefrom.Youdon’tneedthisloopoverallfaces.
Alsothinkhowtrickythingscangetwhenyouwanttogetfilteringovertextureborders,fromonefacetoanother.
1Like
willmotil
December30,2020,12:53pm
#4
Ican’tdirectlyseeanythingwrongeither.
Isecondthecubemappart.YouarealreadyrenderingtherendertargetcubeyoumightaswelljustcastittoatextureCube.
textureCubeDestinationMap=renderTargetCube;
ifyourgoingtodoitthatwaythoughhereisashaderfunctionthatfiguresoutthefacewithoutalltheloopinganddoting.Itspitsoutthefaceviatheout.
Ionlyhavethisbecauseineeditforasphericalmappingconversionfunctioninoneofmyshaders.
Thecubessimplifythingsgreatly.
float2NormalToUvFace(float3v,outintfaceIndex)
{
float3vAbs=abs(v);
floatma;
float2uv;
if(vAbs.z>=vAbs.x&&vAbs.z>=vAbs.y)
{
faceIndex=v.z<0.0?5:4;//FACE_FRONT:FACE_BACK;//zmajoraxis...wedesignatenegativezforward.
ma=0.5f/vAbs.z;
uv=float2(v.z<0.0f?-v.x:v.x,-v.y);
}
elseif(vAbs.y>=vAbs.x)
{
faceIndex=v.y<0.0f?3:2;//FACE_BOTTOM:FACE_TOP;//ymajoraxis.
ma=0.5f/vAbs.y;
uv=float2(v.x,v.y<0.0?-v.z:v.z);
}
else
{
faceIndex=v.x<0.0?1:0;//FACE_LEFT:FACE_RIGHT;//xmajoraxis.
ma=0.5f/vAbs.x;
uv=float2(v.x<0.0?v.z:-v.z,-v.y);
}
returnuv*ma+float2(0.5f,0.5f);
}
Richard_Stern
December31,2020,12:17am
#5
SotherearetworeasonsI’mnotusingaRenderTargetCubebutmaybepeoplehavesolutionstothese:
Ineed8cubes(8pointlights)andthere’snowaytomakeanarrayofrendertargetcubesthatcanbepassedintotheshaderasfarasIcouldtell.Icanmakeanarrayoftexturesthatis8*6(8cubes,6faceseach)sothat’swhyIdiditthisway.
TheCalcPointShadowsPCF()functionneedstosample4pointsandaveragethem,itdoesthisbyoffsettingtheUVsbyonepixel.Iwasn’tsurehowtodothiswithaRenderTargetCubesinceitjustsamplesusingadirectionratherthanUVs.
markus
December31,2020,12:47am
#6
Richard_Stern:
Ineed8cubes(8pointlights)andthere’snowaytomakeanarrayofrendertargetcubesthatcanbepassedintotheshaderasfarasIcouldtell
RenderTargetCubeinheritsfromTexture.Ifanarrayoftexturesworks,shouldn’tthisworktoo?
Richard_Stern
December31,2020,2:20am
#7
RenderTargetCubeinheritsfromTexture.Ifanarrayoftexturesworks,shouldn’tthisworktoo?
TheconstructorforRenderTarget2Dhasanoverloadthatacceptsanarraysize,theoneforRenderTargetCubedoesn’tsothereisnowaytodeclareanarrayofcubesthatwillworkintheshader,andyoucan’tuseaconventionalarraybecausethere’snowaytopassthattotheshader.
Richard_Stern
December31,2020,2:28am
#8
Sotexturearrayvscubeaside(Ireallydon’tthinkthat’stheissuehere):
Here’sanotherdebugscreenshot,Igotittooutputthecontentsoftheshadowmap,readingfromthecorrectface:
ShadowRender21432×80623.9KB
Theactualshadowslookcorrect,itstheareaaroundthemthatdoesn’t.Idon’tthinkthathardseamshouldbethereattheedgeofthetexture.Maybethedepthscaleiswrong?
Richard_Stern
December31,2020,2:47am
#9
Okaywait,nevermindallthat.Justrealisedtheshadowmapisn’trightbecausethespheresshouldbedarkerthanthebackground(theyareclosertothelightandcloserisdarker)
SoaftersomemoredebuggingIhavenarroweddowntheproblemtobeintherenderingoftheshadowmapbutI’mjustnotsurewhy:
IfIsetthelight’spositiontobe9ontheXaxisthenitrendersfine,andasyoucanseeontheshadowmapthespheresaredarkerthanthebackground.(There’sathinseambutthat’sbecauseI’mnotusingacubemap,willworryaboutthatlater)
Shadows11437×803127KB
ThenifImovethelightto10ontheXaxis,thebackgroundoftheshadowmapgoesblack,whichisobviouslywrong:
Shadows21437×803104KB
Thenat11ontheXaxis,thebackgroundoftheshadowmapislighteningagainbutit’sstillwrongbecauseitshouldbelighterthanthespheres:
Shadows31437×803109KB
Sowhatwouldcausethedepthmaptogoblack?HaveIjustwrappedaroundthedepthvaluesorsomething?Howwouldthatevenhappen?
Myrenderingshaderisjust:
#ifOPENGL
#defineSV_POSITIONPOSITION
#defineVS_SHADERMODELvs_3_0
#definePS_SHADERMODELps_3_0
#else
#defineVS_SHADERMODELvs_5_0
#definePS_SHADERMODELps_5_0
#endif
//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
float4x4_World;
float4x4_LightViewProjection;
//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
structVertexShaderOutput
{
float4Position:SV_POSITION;
floatDepth:TEXCOORD0;
};
//----------------------------------------------------------------------------------
//----------------------------------------------------------------------------------
VertexShaderOutputMainVS(float4Position:POSITION0)
{
VertexShaderOutputoutput=(VertexShaderOutput)0;
float4worldPos=mul(Position,_World);
output.Position=mul(worldPos,_LightViewProjection);
output.Depth=output.Position.z/output.Position.w;
returnoutput;
}
float4MainPS(VertexShaderOutputinput):COLOR
{
returnfloat4(input.Depth,0,0,0);
}
//----------------------------------------------------------------------------------
//Techniqueandpasseswithinthetechnique
//----------------------------------------------------------------------------------
techniqueMainEffect
{
passPass0
{
VertexShader=compileVS_SHADERMODELMainVS();
PixelShader=compilePS_SHADERMODELMainPS();
}
};
Anyideas?
markus
December31,2020,3:16am
#10
output.Depth=output.Position.z/output.Position.w;
Ifyoudothew-divisioninthevertexshader,theautomaticdepth-interpolationbetweentheverticeswillbeoff.Iftheverticesareclosetogether,it’snotnoticeable.Iftheyarefarapart,likeinyourgroundplane,itbecomesaproblem.YoucansolveitbypassingPosition.zwtothepixelshaderanddothedepthcalculationthere.
2Likes
Richard_Stern
December31,2020,5:09am
#11
Thatwasit!Thanks!
Workingwith2pointlights:
ShadowsWorking1431×801119KB
Testwith4colouredlights:
ColoredShadows1431×799141KB
4Likes
willmotil
January5,2021,8:48am
#12
Thatisquiteniceineverdidfigureouthowtodopcfifyouwriteupatutorialidreaditforsure.
Richard_Stern
January6,2021,4:45am
#13
IcobbledittogetherfromabunchofthingsIread.I’dsayIunderstanditabout80%whichisnotenoughtoreallywriteaninformedtutorial.Happytosharethecodethough.
Thisismyshader,itusesPBRlightingandhasshadowmappingfor1directionallightandupto8pointlights.
Mysourcesare:
PBRshaderbasedonhttps://learnopengl.com/PBR/Lighting
ShadowmapsbasedonShadowmappingonMonogame
Afewcaveats:Itisdefinitelynotthemostefficientorwellwrittenshader,myfocuswasjustongettingitworking.Alsoitusesanarrayof6texturestodothepointlightshadowsinsteadofacubemapwhichleadstoseamsalongtheedges.
Sowithallthatsaid,here’sthecode
//----------------------------------------------------------------------------------
//Standarddefines
//----------------------------------------------------------------------------------
#ifOPENGL
#defineSV_POSITIONPOSITION
#defineVS_SHADERMODELvs_3_0
#definePS_SHADERMODELps_3_0
#else
#defineVS_SHADERMODELvs_5_0
#definePS_SHADERMODELps_5_0
#endif
//----------------------------------------------------------------------------------
//Properties
//----------------------------------------------------------------------------------
float4x4_World;
float4x4_View;
float4x4_Projection;
float3_CameraPos;
float4_Color;
Texture2D_Albedo;
Texture2D_Metalness;
Texture2D_Displacement;
Texture2D_Normal;
Texture2D_Roughness;
Texture2D_AO;
//Lights
staticconstintMaxDirectionalLights=8;
staticconstintMaxPointLights=8;
staticconstintCubeSides=6;
staticconstfloat3FaceDirectons[CubeSides]={
float3(1,0,0),
float3(-1,0,0),
float3(0,1,0),
float3(0,-1,0),
float3(0,0,1),
float3(0,0,-1),
};
float3_AmbientColor;
float3_DirectionalLights[MaxDirectionalLights];
float3_DirectionalColors[MaxDirectionalLights];
float_DirectionalIntensity[MaxDirectionalLights];
float3_PointLightPos[MaxPointLights];
float3_PointLightColors[MaxPointLights];
float_PointLightIntensity[MaxPointLights];
float_PointLightRange[MaxPointLights];
//Shadows
Texture2D_DirectionalShadowMap;
SamplerStateDirectionalShadowMapSampler
{
Texture=(_DirectionalShadowMap);
MinFilter=point;
MagFilter=point;
MipFilter=point;
AddressU=Wrap;
AddressV=Wrap;
};
Texture2DArray_PointShadowMap;
SamplerStatePointShadowMapSampler
{
Texture=(_PointShadowMap);
MinFilter=point;
MagFilter=point;
MipFilter=point;
AddressU=Wrap;
AddressV=Wrap;
};
float4x4_LightSpaceMatrix;
float4x4_PointLightSpaceMatrix[MaxPointLights*CubeSides];
int_ShadowMapSize;
float_DepthBias;
int_DirectionalShadowIndex;
//----------------------------------------------------------------------------------
staticconstfloatPI=3.14159265359;
//----------------------------------------------------------------------------------
//Requiredattributesoftheinputvertices
//----------------------------------------------------------------------------------
structVertexShaderInput
{
float3Position:POSITION0;
float3Normal:NORMAL;
//float3Tangent:TANGENT;
//float3Binormal:BINORMAL;
float2TextureUV:TEXCOORD0;
};
//Semanticsforoutputofvertexshader/inputofpixelshader
structVertexShaderOutput
{
float4Position:SV_POSITION;
float2TextureUV:TEXCOORD0;
float3Normal:TEXCOORD1;
//float3Tangent:TEXCOORD2;
//float3Binormal:TEXCOORD3;
float3WorldPosition:TEXCOORD2;
};
SamplerStateMeshTextureSampler
{
Filter=Anisotropic;
AddressU=Wrap;
AddressV=Wrap;
};
//----------------------------------------------------------------------------------
//PBRequations
//----------------------------------------------------------------------------------
float3fresnelSchlick(floatcosTheta,float3F0)
{
cosTheta=min(cosTheta,1.0);
returnF0+(1.0-F0)*pow(1.0-cosTheta,5.0);
}
floatDistributionGGX(float3N,float3H,floatroughness)
{
floata=roughness*roughness;
floata2=a*a;
floatNdotH=max(dot(N,H),0.0);
floatNdotH2=NdotH*NdotH;
floatnum=a2;
floatdenom=(NdotH2*(a2-1.0)+1.0);
denom=PI*denom*denom;
returnnum/denom;
}
floatGeometrySchlickGGX(floatNdotV,floatroughness)
{
floatr=(roughness+1.0);
floatk=(r*r)/8.0;
floatnum=NdotV;
floatdenom=NdotV*(1.0-k)+k;
returnnum/denom;
}
floatGeometrySmith(float3N,float3V,float3L,floatroughness)
{
floatNdotV=max(dot(N,V),0.0);
floatNdotL=max(dot(N,L),0.0);
floatggx2=GeometrySchlickGGX(NdotV,roughness);
floatggx1=GeometrySchlickGGX(NdotL,roughness);
returnggx1*ggx2;
}
//Generatingtangentsandbinormalsbasedonhttps://github.com/JoeyDeVries/LearnOpenGL/blob/master/src/6.pbr/1.2.lighting_textured/1.2.pbr.fs
float3getNormalFromMap(VertexShaderOutputinput)
{
float3tangentNormal=_Normal.Sample(MeshTextureSampler,input.TextureUV).rgb;
tangentNormal=normalize(tangentNormal*2.0-1.0);
float3Q1=ddx(input.WorldPosition);
float3Q2=ddy(input.WorldPosition);
float2st1=ddx(input.TextureUV);
float2st2=ddy(input.TextureUV);
float3N=normalize(input.Normal);
float3T=-normalize(Q1*st2.y-Q2*st1.y);
float3B=normalize(cross(N,T));
//float3N=normalize(input.Normal);
//float3T=normalize(input.Tangent);
//float3B=normalize(input.Binormal);
float3x3TBN=float3x3(T,B,N);
returnnormalize(mul(tangentNormal,TBN));
}
//----------------------------------------------------------------------------------
//Directionallights:CalculatestheshadowtermusingPCF
//----------------------------------------------------------------------------------
floatCalcDirectionalShadowsPCF(floatlight_space_depth,floatndotl,float2shadow_coord)
{
floatshadow_term=0;
floatvariableBias=clamp(0.001*tan(acos(ndotl)),0,_DepthBias);
//safetoassumeit'sasquare
floatsize=1.0/_ShadowMapSize;
floatsamples[4];
samples[0]=(light_space_depth-variableBias<_directionalshadowmap.sample samples shadow_term="(samples[0]+samples[1]+samples[2]+samples[3])/4.0;" returnshadow_term floatcalcpointshadowspcf floatshadow_term="0;" floatvariablebias="clamp(0.001*tan(acos(ndotl)),0,_DepthBias);" floatsize="1.0/_ShadowMapSize;" floatsamples float3calculatepointlights float3v="normalize(_CameraPos-worldPosition);" float3f0="float3(0.04,0.04,0.04);" f0="lerp(F0,albedo,metallic);" float3lo="float3(0.0,0.0,0.0);" for float3l="normalize(_PointLightPos[i]-worldPosition);" float3h="normalize(V+L);" floatdistance="length(_PointLightPos[i]-worldPosition);" floatattenuation="_PointLightRange[i]/(distance*distance);" float3radiance="_PointLightColors[i]*attenuation*_PointLightIntensity[i];" floatndf="DistributionGGX(N,H,roughness);" floatg="GeometrySmith(N,V,L,roughness);" float3f="fresnelSchlick(max(dot(H,V),0.0),F0);" float3ks="F;" float3kd="float3(1.0,1.0,1.0)-kS;" kd float3numerator="NDF*G*F;" floatdenominator="4.0*max(dot(N,V),0.0)*max(dot(N,L),0.0);" float3specular="numerator/max(denominator,0.001);" floatndotl="max(dot(N,L),0.0);" floatshadowcontribution="1.0;" float3directiontofragment="normalize(worldPosition-_PointLightPos[i]);" floatclosestdirection="-1;" intfaceindex="0;" float3forward="FaceDirectons[face];" floatresult="dot(directionToFragment,forward);" if>closestDirection)
{
closestDirection=result;
faceIndex=face;
}
}
intarrayIndex=i*CubeSides+faceIndex;
float4lightingPosition=mul(float4(worldPosition,1),_PointLightSpaceMatrix[arrayIndex]);
float2shadowTexCoord=mad(0.5,lightingPosition.xy/lightingPosition.w,float2(0.5,0.5));
shadowTexCoord.y=1.0f-shadowTexCoord.y;
floatourDepth=(lightingPosition.z/lightingPosition.w);
shadowContribution=CalcPointShadowsPCF(ourDepth,NdotL,float3(shadowTexCoord,arrayIndex));
Lo+=(kD*albedo/PI+specular)*radiance*NdotL*shadowContribution;
}
returnLo;
}
float3CalculateDirectionalLights(float3worldPosition,float3N,float3albedo,floatmetallic,floatroughness)
{
float3V=normalize(_CameraPos-worldPosition);
//Calculatesurfacereflectionatzeroincidence,defaultto0.04butadjustformetallicsurfaces.
float3F0=float3(0.04,0.04,0.04);
F0=lerp(F0,albedo,metallic);
//Reflectionequation
float3Lo=float3(0.0,0.0,0.0);
for(inti=0;i