From be611687a8b29e0bdb118a93d35e24cd9521b403 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Sat, 22 Aug 2020 02:01:41 -0700 Subject: [PATCH] rendering g-buffer --- Effects/FXB/GBufferEffect.fxb | Bin 0 -> 7476 bytes Effects/GBufferEffect.cs | 241 ++++++++++++++++++++++++++++++++++ Effects/HLSL/GBufferEffect.fx | 203 ++++++++++++++++++++++++++++ Kav.Core.csproj | 3 + Kav.Framework.csproj | 3 + Loaders/ModelLoader.cs | 2 +- Renderer.cs | 108 +++++++++++++++ Resources.cs | 12 ++ 8 files changed, 571 insertions(+), 1 deletion(-) create mode 100644 Effects/FXB/GBufferEffect.fxb create mode 100644 Effects/GBufferEffect.cs create mode 100644 Effects/HLSL/GBufferEffect.fx diff --git a/Effects/FXB/GBufferEffect.fxb b/Effects/FXB/GBufferEffect.fxb new file mode 100644 index 0000000000000000000000000000000000000000..9b2d6844f486894e8a28f804c05221fee8d81938 GIT binary patch literal 7476 zcmds6O=w(I6u$4hX)BVR!9iS95J^!%L}{9Y+Q3v}lh%NRCMHuGnP!HWq(*S@ zgf3iUF{q0!x_Dqf>9{E9BDkpw5er?E;-VD65oh5-(3J?5jNfR%7Pd%%bDZ{?;7W4VR7`BD!0E--zIU?CrZ z)FC!LS;vwUj^a54ei+VEu1Crj*?)FC!L z-G1DFejUtywA9(AA5XKOr5$)a20oUXo6P5Drj8fp-#$B=E0>#Hisxq0Dc6NE8}(@# z{uAKLI`vEUI3KM37Jn>>O>{ncz-FFLPUh!xdLC&{6D&)tLaN~$%h*f_Gxv0TX^k?y zco)xuEyTXi{R^iIaUuU(;#|H_DCMW|ly>$}u(Va}Jc9akJHxZ*bER@_tTZ`WE*8p+ ze`$Ad8`|9`?Vg;;y*pAWypx-nn<>mH&NIh$%eI+@%$VYHCzxw;H<*||04A1vH!^y- zRGNHG;{jD=EC_t(Vo#ZHv3j~N)+KIP_sHn*?DWi3t_)dV9kF%6)KVAwFnV@!I#)uO z+=A}=Uf3LW7E!dxxU|9kNIbHSuMEteIfK+dKiWC22f?iCQLs=Sgj1s6xd@INeHn7y z(f6UU1JV%eS;z~Heg*P%NBr$Us+mg2VuqIyN&^DwGRn<}wG4Kb1L}9nEG;z-0yt=ZZ7fOEL%ho;j$h)z>lnK=nqZ z>F|(r;c*NP^AciaDn|A~4>Dp7Q$e|2A!FV!4Icbd2>-xKcWc?a$2+almvTBp;fS*5 z$iDH>c3<01XgNr;9pfLQ{SXaVdylTC(J*UR$DzjUKPt{rBW|Bf*WEN1>1n8|u0 zW+Eror>9P6oVf4Z;l%5;Hyq>iCnp=W6dr=p?~wCg!D#{Vhv19aX4c;4*0zkN$#Pxl z24qqV>{~p=?{4rkN3hj-wCy>!&DxIR!Bo^K>~6m8N9MVTVS&O!2H(~BwjJ&sa6hG| z(yX^PwVV!pb)v!?jGP@R)d1H+zC+%oTrp*gAF#pyZeUy&-w&Q_`i7M4dtIlmeXqgy zX2`PCmAZ!VH_>igmknTp91frlzB0MNL!5~}-=T9+fcRlQ=^vmh`mO6^Khdb}6YSc? zKKSY~+Nx{2$x+NFb>O`%|1o24my-f@IilU{KSoZ*cc2>DXT4m-dYG$texS2i z1JA{4;8U2Ax#|*3Lo|b#tAK|lt_#Uki`LfZUK7XVHsq=%PuTwnN%2T=l=jUGLEgJ{@SfW>@3&a)cF2p)*IVT$X>N5uIC|kLoa~S#`se6>e z81HWUx65hD$!Q|5m%Hf0HNo%T8qv^eVklk{zraKrm&<<2AjUS&&U8-ACwEkwycfMCk%ro~P=VJ=HL+;1ioACt`-VggW;to0W-t5~0 z2%n8|yywKc$jN=&!#>V9=bcE6fve+uFpl$|U`odMEtnYRjQ!*{qQrTGccd#|iSzHZ zMdCcM3AZ^%$~WR>_wDVB@vYA5|A%-7oY8-k<2MuklIP90waMq$hcz*FNj%!P)Bw@O zrEm!X&eHMyr}D>-tTbMW*G8{)96v%|y$3+=0T0>ox-8@ga1D&h9o-(!K6k*)`%r#s znNt(Xw5R6xuYKR&>ervp!-O6s^luV+twne5UZrmwZabP=*!*SM3T`F_v%_PoXQi=6 z9U0FKvR?3B`_yxvfz1aCrL6D=s29YXB}B|b=AEhvR&YPr+^c`)OW26H#!0EnZOF1$ zGiOae=Y0tCm9J9BSr5fF-jfl#lzJkznZF-Ys{uSy_{Y9)1-1Wsfwm@Y^UrsBjajuh zCqypO=TOW?i2D_LCRHK6N9C@IJ~jID+=_2bD|_$}*$*-H8t$P2+)UKCSA!#?`&!3$ zv(-~t$G2^S_{MjySL5AhD%O3i?}+u3*1K$ZM29Y)j&)Q2&R93~?~3)b_TLR1xcKZR p+O9B47%P$k6o|NO^!GTH!Cb|}GTbj)_8aK*bK^k#TE1)G_YchewfX=6 literal 0 HcmV?d00001 diff --git a/Effects/GBufferEffect.cs b/Effects/GBufferEffect.cs new file mode 100644 index 0000000..ff8769d --- /dev/null +++ b/Effects/GBufferEffect.cs @@ -0,0 +1,241 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace Kav +{ + public class GBufferEffect : Effect, TransformEffect + { + EffectParameter worldParam; + EffectParameter worldViewProjectionParam; + EffectParameter worldInverseTransposeParam; + + EffectParameter albedoTextureParam; + EffectParameter normalTextureParam; + EffectParameter metallicRoughnessTextureParam; + + EffectParameter albedoParam; + EffectParameter metallicParam; + EffectParameter roughnessParam; + + EffectParameter shaderIndexParam; + + Matrix world = Matrix.Identity; + Matrix view = Matrix.Identity; + Matrix projection = Matrix.Identity; + + Vector3 albedo; + float metallic; + float roughness; + + bool albedoTextureEnabled = false; + bool metallicRoughnessMapEnabled = false; + bool normalMapEnabled = false; + + EffectDirtyFlags dirtyFlags = EffectDirtyFlags.All; + + public Matrix World + { + get { return world; } + set + { + world = value; + dirtyFlags |= EffectDirtyFlags.World | EffectDirtyFlags.WorldViewProj; + } + } + + public Matrix View + { + get { return view; } + set + { + view = value; + dirtyFlags |= EffectDirtyFlags.WorldViewProj | EffectDirtyFlags.EyePosition; + } + } + + public Matrix Projection + { + get { return projection; } + set + { + projection = value; + dirtyFlags |= EffectDirtyFlags.WorldViewProj; + } + } + + public Vector3 Albedo + { + get { return albedo; } + set + { + albedo = value; + albedoParam.SetValue(albedo); + } + } + + public float Metallic + { + get { return metallic; } + set + { + metallic = value; + metallicParam.SetValue(metallic); + } + } + + public float Roughness + { + get { return roughness; } + set + { + roughness = value; + roughnessParam.SetValue(roughness); + } + } + + public Texture2D AlbedoTexture + { + get { return albedoTextureParam.GetValueTexture2D(); } + set + { + albedoTextureParam.SetValue(value); + albedoTextureEnabled = value != null; + dirtyFlags |= EffectDirtyFlags.ShaderIndex; + } + } + + public Texture2D NormalTexture + { + get { return normalTextureParam.GetValueTexture2D(); } + set + { + normalTextureParam.SetValue(value); + normalMapEnabled = value != null; + dirtyFlags |= EffectDirtyFlags.ShaderIndex; + } + } + + public Texture2D MetallicRoughnessTexture + { + get { return metallicRoughnessTextureParam.GetValueTexture2D(); } + set + { + metallicRoughnessTextureParam.SetValue(value); + metallicRoughnessMapEnabled = value != null; + dirtyFlags |= EffectDirtyFlags.ShaderIndex; + } + } + + public GBufferEffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.GBufferEffect) + { + CacheEffectParameters(); + } + + protected GBufferEffect(GBufferEffect cloneSource) : base(cloneSource) + { + CacheEffectParameters(); + + World = cloneSource.World; + View = cloneSource.View; + Projection = cloneSource.Projection; + + AlbedoTexture = cloneSource.AlbedoTexture; + NormalTexture = cloneSource.NormalTexture; + MetallicRoughnessTexture = cloneSource.MetallicRoughnessTexture; + + Albedo = cloneSource.Albedo; + Metallic = cloneSource.Metallic; + Roughness = cloneSource.Roughness; + } + + public override Effect Clone() + { + return new GBufferEffect(this); + } + + protected override void OnApply() + { + if ((dirtyFlags & EffectDirtyFlags.World) != 0) + { + worldParam.SetValue(world); + + Matrix.Invert(ref world, out Matrix worldInverse); + Matrix.Transpose(ref worldInverse, out Matrix worldInverseTranspose); + worldInverseTransposeParam.SetValue(worldInverseTranspose); + + dirtyFlags &= ~EffectDirtyFlags.World; + } + + if ((dirtyFlags & EffectDirtyFlags.WorldViewProj) != 0) + { + Matrix.Multiply(ref world, ref view, out Matrix worldView); + Matrix.Multiply(ref worldView, ref projection, out Matrix worldViewProj); + worldViewProjectionParam.SetValue(worldViewProj); + + dirtyFlags &= ~EffectDirtyFlags.WorldViewProj; + } + + if ((dirtyFlags & EffectDirtyFlags.EyePosition) != 0) + { + Matrix.Invert(ref view, out Matrix inverseView); + + dirtyFlags &= ~EffectDirtyFlags.EyePosition; + } + + if ((dirtyFlags & EffectDirtyFlags.ShaderIndex) != 0) + { + int shaderIndex = 0; + + if (albedoTextureEnabled && metallicRoughnessMapEnabled && normalMapEnabled) + { + shaderIndex = 7; + } + else if (metallicRoughnessMapEnabled && normalMapEnabled) + { + shaderIndex = 6; + } + else if (albedoTextureEnabled && normalMapEnabled) + { + shaderIndex = 5; + } + else if (albedoTextureEnabled && metallicRoughnessMapEnabled) + { + shaderIndex = 4; + } + else if (normalMapEnabled) + { + shaderIndex = 3; + } + else if (metallicRoughnessMapEnabled) + { + shaderIndex = 2; + } + else if (albedoTextureEnabled) + { + shaderIndex = 1; + } + + shaderIndexParam.SetValue(shaderIndex); + + dirtyFlags &= ~EffectDirtyFlags.ShaderIndex; + } + } + + void CacheEffectParameters() + { + worldParam = Parameters["World"]; + worldViewProjectionParam = Parameters["WorldViewProjection"]; + worldInverseTransposeParam = Parameters["WorldInverseTranspose"]; + + albedoTextureParam = Parameters["AlbedoTexture"]; + normalTextureParam = Parameters["NormalTexture"]; + metallicRoughnessTextureParam = Parameters["MetallicRoughnessTexture"]; + + albedoParam = Parameters["AlbedoValue"]; + metallicParam = Parameters["MetallicValue"]; + roughnessParam = Parameters["RoughnessValue"]; + + shaderIndexParam = Parameters["ShaderIndex"]; + } + } +} diff --git a/Effects/HLSL/GBufferEffect.fx b/Effects/HLSL/GBufferEffect.fx new file mode 100644 index 0000000..63cb2ce --- /dev/null +++ b/Effects/HLSL/GBufferEffect.fx @@ -0,0 +1,203 @@ +#include "Macros.fxh" + +DECLARE_TEXTURE(AlbedoTexture, 0); +DECLARE_TEXTURE(NormalTexture, 1); +DECLARE_TEXTURE(MetallicRoughnessTexture, 2); + +BEGIN_CONSTANTS + + float3 AlbedoValue _ps(c0) _cb(c0); + float MetallicValue _ps(c1) _cb(c1); + float RoughnessValue _ps(c2) _cb(c2); + +MATRIX_CONSTANTS + + float4x4 World _vs(c0) _cb(c7); + float4x4 WorldInverseTranspose _vs(c4) _cb(c11); + float4x4 WorldViewProjection _vs(c8) _cb(c15); + +END_CONSTANTS + +struct VertexInput +{ + float4 Position : POSITION; + float3 Normal : NORMAL; + float2 TexCoord : TEXCOORD0; +}; + +struct PixelInput +{ + float4 Position : SV_POSITION; + float3 PositionWorld : TEXCOORD0; + float3 NormalWorld : TEXCOORD1; + float2 TexCoord : TEXCOORD2; +}; + +struct PixelOutput +{ + float4 gPosition : COLOR0; + float4 gNormal : COLOR1; + float4 gAlbedo : COLOR2; + float4 gMetallicRoughness : COLOR3; +}; + +// Vertex Shader + +PixelInput main_vs(VertexInput input) +{ + PixelInput output; + + output.PositionWorld = mul(input.Position, World).xyz; + output.NormalWorld = mul(input.Normal, (float3x3)WorldInverseTranspose).xyz; + output.TexCoord = input.TexCoord; + output.Position = mul(input.Position, WorldViewProjection); + + return output; +} + +// Pixel Shaders + +// Easy trick to get tangent-normals to world-space to keep PBR code simplified. +float3 GetNormalFromMap(float3 worldPos, float2 texCoords, float3 normal) +{ + float3 tangentNormal = SAMPLE_TEXTURE(NormalTexture, texCoords).xyz * 2.0 - 1.0; + + float3 Q1 = ddx(worldPos); + float3 Q2 = ddy(worldPos); + float2 st1 = ddx(texCoords); + float2 st2 = ddy(texCoords); + + float3 N = normalize(normal); + float3 T = normalize(Q1*st2.y - Q2*st1.y); + float3 B = -normalize(cross(N, T)); + float3x3 TBN = float3x3(T, B, N); + + return normalize(mul(tangentNormal, TBN)); +} + +PixelOutput NonePS(PixelInput input) +{ + PixelOutput output; + + output.gPosition = float4(input.PositionWorld, 0.0); + output.gNormal = float4(normalize(input.NormalWorld), 0.0); + output.gAlbedo = float4(AlbedoValue, 1.0); + output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 0.0); + + return output; +} + +PixelOutput AlbedoPS(PixelInput input) +{ + PixelOutput output; + + output.gPosition = float4(input.PositionWorld, 0.0); + output.gNormal = float4(normalize(input.NormalWorld), 0.0); + output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord); + output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 0.0); + + return output; +} + +PixelOutput MetallicRoughnessPS(PixelInput input) +{ + PixelOutput output; + + output.gPosition = float4(input.PositionWorld, 0.0); + output.gNormal = float4(normalize(input.NormalWorld), 0.0); + output.gAlbedo = float4(AlbedoValue, 1.0); + output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord); + + return output; +} + +PixelOutput NormalPS(PixelInput input) +{ + PixelOutput output; + + output.gPosition = float4(input.PositionWorld, 0.0); + output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 0.0); + output.gAlbedo = float4(AlbedoValue, 1.0); + output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 0.0); + + return output; +} + +PixelOutput AlbedoMetallicRoughnessPS(PixelInput input) +{ + PixelOutput output; + + output.gPosition = float4(input.PositionWorld, 0.0); + output.gNormal = float4(normalize(input.NormalWorld), 0.0); + output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord); + output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord); + + return output; +} + +PixelOutput AlbedoNormalPS(PixelInput input) +{ + PixelOutput output; + + output.gPosition = float4(input.PositionWorld, 0.0); + output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 0.0); + output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord); + output.gMetallicRoughness = float4(MetallicValue, RoughnessValue, 0.0, 0.0); + + return output; +} + +PixelOutput MetallicRoughnessNormalPS(PixelInput input) +{ + PixelOutput output; + + output.gPosition = float4(input.PositionWorld, 0.0); + output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 0.0); + output.gAlbedo = float4(AlbedoValue, 1.0); + output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord); + + return output; +} + +PixelOutput AlbedoMetallicRoughnessNormalMapPS(PixelInput input) +{ + PixelOutput output; + + output.gPosition = float4(input.PositionWorld, 0.0); + output.gNormal = float4(GetNormalFromMap(input.PositionWorld, input.TexCoord, input.NormalWorld), 0.0); + output.gAlbedo = SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord); + output.gMetallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord); + + return output; +} + +PixelShader PSArray[8] = +{ + compile ps_3_0 NonePS(), + + compile ps_3_0 AlbedoPS(), + compile ps_3_0 MetallicRoughnessPS(), + compile ps_3_0 NormalPS(), + + compile ps_3_0 AlbedoMetallicRoughnessPS(), + compile ps_3_0 AlbedoNormalPS(), + compile ps_3_0 MetallicRoughnessNormalPS(), + + compile ps_3_0 AlbedoMetallicRoughnessNormalMapPS() +}; + +int PSIndices[8] = +{ + 0, 1, 2, 3, 4, 5, 6, 7 +}; + +int ShaderIndex = 0; + +Technique GBuffer +{ + Pass + { + VertexShader = compile vs_3_0 main_vs(); + PixelShader = (PSArray[PSIndices[ShaderIndex]]); + } +} diff --git a/Kav.Core.csproj b/Kav.Core.csproj index 812b812..e143f89 100644 --- a/Kav.Core.csproj +++ b/Kav.Core.csproj @@ -15,6 +15,9 @@ + + Kav.Resources.GBufferEffect.fxb + Kav.Resources.PBREffect.fxb diff --git a/Kav.Framework.csproj b/Kav.Framework.csproj index 592a8ff..3d70199 100644 --- a/Kav.Framework.csproj +++ b/Kav.Framework.csproj @@ -15,6 +15,9 @@ + + Kav.Resources.GBufferEffect.fxb + Kav.Resources.PBREffect.fxb diff --git a/Loaders/ModelLoader.cs b/Loaders/ModelLoader.cs index fc5b206..2655368 100644 --- a/Loaders/ModelLoader.cs +++ b/Loaders/ModelLoader.cs @@ -15,7 +15,7 @@ namespace Kav foreach (var meshPartData in meshData.MeshParts) { - var effect = new Kav.PBREffect( + var effect = new Kav.GBufferEffect( graphicsDevice ) { diff --git a/Renderer.cs b/Renderer.cs index 77037b3..f31419e 100644 --- a/Renderer.cs +++ b/Renderer.cs @@ -13,6 +13,17 @@ namespace Kav private RenderTarget2D DepthRenderTarget { get; } private SimpleDepthEffect SimpleDepthEffect { get; } + private RenderTarget2D gPosition { get; } + private RenderTarget2D gNormal { get; } + private RenderTarget2D gAlbedo { get; } + private RenderTarget2D gMetallicRoughness { get; } + + private RenderTargetBinding[] GBuffer { get; } + + private GBufferEffect GBufferEffect { get; } + + private SpriteBatch SpriteBatch { get; } + public Renderer(GraphicsDevice graphicsDevice, int renderDimensionsX, int renderDimensionsY) { GraphicsDevice = graphicsDevice; @@ -28,7 +39,104 @@ namespace Kav DepthFormat.Depth24 ); + gPosition = new RenderTarget2D( + GraphicsDevice, + renderDimensionsX, + renderDimensionsY, + false, + SurfaceFormat.Color, + DepthFormat.None + ); + + gNormal = new RenderTarget2D( + GraphicsDevice, + renderDimensionsX, + renderDimensionsY, + false, + SurfaceFormat.Color, + DepthFormat.None + ); + + gAlbedo = new RenderTarget2D( + GraphicsDevice, + renderDimensionsX, + renderDimensionsY, + false, + SurfaceFormat.Color, + DepthFormat.None + ); + + gMetallicRoughness = new RenderTarget2D( + GraphicsDevice, + renderDimensionsX, + renderDimensionsY, + false, + SurfaceFormat.Color, + DepthFormat.None + ); + + GBuffer = new RenderTargetBinding[4] { + new RenderTargetBinding(gPosition), + new RenderTargetBinding(gNormal), + new RenderTargetBinding(gAlbedo), + new RenderTargetBinding(gMetallicRoughness) + }; + SimpleDepthEffect = new SimpleDepthEffect(GraphicsDevice); + + SpriteBatch = new SpriteBatch(GraphicsDevice); + } + + public void DeferredRender( + Camera camera, + IEnumerable<(Model, Matrix)> modelTransforms, + IEnumerable pointLights, + IEnumerable directionalLights + ) { + GraphicsDevice.SetRenderTargets(GBuffer); + GraphicsDevice.Clear(Color.Black); + + foreach (var (model, transform) in modelTransforms) + { + foreach (var modelMesh in model.Meshes) + { + foreach (var meshPart in modelMesh.MeshParts) + { + GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer); + GraphicsDevice.Indices = meshPart.IndexBuffer; + + if (meshPart.Effect is TransformEffect transformEffect) + { + transformEffect.World = transform; + transformEffect.View = camera.View; + transformEffect.Projection = camera.Projection; + } + + foreach (var pass in meshPart.Effect.CurrentTechnique.Passes) + { + pass.Apply(); + + GraphicsDevice.DrawIndexedPrimitives( + PrimitiveType.TriangleList, + 0, + 0, + meshPart.VertexBuffer.VertexCount, + 0, + meshPart.Triangles.Length + ); + } + } + } + } + + GraphicsDevice.SetRenderTarget(null); + GraphicsDevice.Clear(Color.Black); + SpriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null); + SpriteBatch.Draw(gPosition, new Rectangle(0, 0, 640, 360), Color.White); + SpriteBatch.Draw(gAlbedo, new Rectangle(640, 0, 640, 360), Color.White); + SpriteBatch.Draw(gNormal, new Rectangle(0, 360, 640, 360), Color.White); + SpriteBatch.Draw(gMetallicRoughness, new Rectangle(640, 360, 640, 360), Color.White); + SpriteBatch.End(); } public void Render( diff --git a/Resources.cs b/Resources.cs index 6ff3806..293307d 100644 --- a/Resources.cs +++ b/Resources.cs @@ -4,6 +4,17 @@ namespace Kav { internal class Resources { + public static byte[] GBufferEffect + { + get + { + if (gBufferEffect == null) + { + gBufferEffect = GetResource("GBufferEffect"); + } + return gBufferEffect; + } + } public static byte[] PBREffect { get @@ -28,6 +39,7 @@ namespace Kav } } + private static byte[] gBufferEffect; private static byte[] pbrEffect; private static byte[] simpleDepthEffect;