我的专栏目录:
小IVan:专题概述及目录然后是老规矩先上效果:
![v2-8b0799c0cdc968ee845ca73d0fad0645_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/f27157e85e6d1cbd0403734b8520e66d.jpeg)
![v2-8aa12345d381aac9ee6f374d64a54f7f_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/a6b0a44d431a3cf505b5656d3fd20354.jpeg)
![v2-782b32576bf82280259d1ed11de3baf9_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/cd32fd4d0ac260f5c6b86bac999bee75.jpeg)
要实现次世代卡通效果我们需要以下步骤
(1)先做出正确的PBR效果
(2)对渲染进行风格化
(3)改进优化
第一点首先要做出正确的PBR效果
这一步在虚幻里简直就是天生的优势,因为虚幻的PBR渲染管线是非常标准的,我们要基于这个框架来进行风格化修改。虚幻的PBR框架我前面的文章有详细描述,这里我就不多解释了。
第二点对渲染进行风格化
我们搞清楚第一步以后,我们需要对渲染进行风格化,首先我们要风格化光影着色,前面第一第二章已经做了这个事情了
下面是高光模型的风格化处理
![v2-d95fc757dd861a143ab2ba3a63681ecb_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/84f5a06fa54748d6020c35295d8db1ec.jpeg)
然后是阴影计算的风格化处理
![v2-a5301a5672c1cd28cbe7743b325ef5be_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/32047abeae18b3009e30ebfcc32b04f7.jpeg)
然后我对IBL进行风格化处理
![v2-19021b372ca73cb1824c6286e000e1f4_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/adee6e51492de9cc049af0a75ddca7c7.jpeg)
这是我的环境图,我对它进行了风格化处理
然后我们需要对菲尼尔项进行风格化处理,菲尼尔项被风格化处理成了勾线。
勾线用了两种种办法
第一种是屏幕空间的勾线,用法线和深度来查找边缘,这个在第二卷有详细说明。但是这种办法会出一些问题,就是内部会有杂线
![v2-2e1509fbc0fffae9ea8fa082a09cb23d_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/a818d3099f1364d2a3ff78388a136b7d.jpeg)
我的解决方法是再渲染一个mask,来遮蔽掉这些杂线
![v2-e195a7fda79453e8e98e37dd2a11687e_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/018e67b56ca00063d60a3f5337022285.jpeg)
![v2-fd0b1097694a2d7130eedba99800f343_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/f1abd57c3feba62f0311d1621e0a8b9d.jpeg)
然后是光影了,人物面部模型可能会产生一些奇怪的影子
![v2-22cb485f98cd39953f7b844c6bd5f554_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/1a0b017e262b758f96b5aa5d374f22a1.jpeg)
同样我渲染了一个遮罩来解决这个问题
![v2-b3e3d7cf958a785b35e92efae04a331e_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/6b26856eb3f2dffa8ff17fecd4727ff1.jpeg)
![v2-3a6e17df2c623a0725924d937e5d21ec_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/e49d1f740c0d74e29b976b8ed79643ba.jpeg)
然后是处理大体的勾线了,我法线基于屏幕空间的勾线其实不是很好控制,于是我换了另一种方式来处理。把模型渲染两次然后沿着法线在屏幕空间把模型挤出,这样渲染一条边缘
![v2-fc9f25cc17e597eb2f05bf48e4236852_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/ddbc682b627c5901bb09f64a057522af.jpeg)
仅屏幕空间勾线:
![v2-0a106f6ae016d044cf5cdb56baea583b_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/bdc2fa48eaaad9ef110b8498a073d0b3.jpeg)
渲染二次模型勾线:
![v2-9a0884621422f4aacd4ab46f9743decf_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/1408fd70eeca783735f2f1fd37fd2ad7.jpeg)
把高光去再风格化一下试试:
![v2-608b88a0ef7235a4d761cf74c40ff612_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/81d6e05a90670fb5045611a7cc2d16ad.jpeg)
头发上的杂线我不想处理掉,我感觉这样留点笔触的感觉也挺好,后面会对皮肤光照模型和头发光照模型进行风格化改造。
完整代码修改如下:
ShadingMode.ush
![v2-cf672ac8bfbee08216fccdeb3af19453_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/b3e7826039aa396e3bdaa4124c252bd8.jpeg)
DefrredLightingCommon.ush
![v2-e78a1437fa1a8a7e3bdc7fd9c5c12639_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/691496456d458cb0e90f565d90e70c3a.jpeg)
ToonOutline.usf
![v2-6e7c78b2233fb83cc0458c390fd1bbbc_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/bde4408218a4bc938945eade05d18876.jpeg)
![v2-9442739af696d095493269a2f9ba9ef7_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/541bb1346c2536fd24545809b46eee73.jpeg)
ToonOutLineRendering.cpp
#include "DeferredShadingRenderer.h"
#include "AtmosphereRendering.h"
#include "ScenePrivate.h"
#include "Engine/TextureCube.h"
#include "PipelineStateCache.h"
#include "SceneView.h"
DECLARE_GPU_STAT(ToonOutLine);
class FToonOutLineVS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FToonOutLineVS, Global);
public:
FToonOutLineVS(){}
FToonOutLineVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) :
FGlobalShader(Initializer)
{
//这里做绑定
}
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM4);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
}
virtual bool Serialize(FArchive& Ar) override
{
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
return bShaderHasOutdatedParameters;
}
private:
//成员
};
IMPLEMENT_SHADER_TYPE(, FToonOutLineVS, TEXT("/Engine/Private/ToonOutline.usf"), TEXT("MainVS"), SF_Vertex);
class FToonOutLinePS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FToonOutLinePS, Global);
public:
FToonOutLinePS() {}
FToonOutLinePS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) :
FGlobalShader(Initializer)
{
//这里做绑定
GBufferATextureVal.Bind(Initializer.ParameterMap, TEXT("GBufferATexture"));
GBufferATextureSampler.Bind(Initializer.ParameterMap, TEXT("GBufferATextureSampler"));
DepthTextureVal.Bind(Initializer.ParameterMap, TEXT("DepthTexture"));
DepthTextureSampler.Bind(Initializer.ParameterMap, TEXT("DepthTextureSampler"));
SceneTextureVal.Bind(Initializer.ParameterMap, TEXT("SceneTexture"));
SceneTextureSampler.Bind(Initializer.ParameterMap, TEXT("SceneTextureSampler"));
CustomDataTextureVal.Bind(Initializer.ParameterMap, TEXT("CustomDataTexture"));
CustomDataTextureSampler.Bind(Initializer.ParameterMap, TEXT("CustomDataTextureSampler"));
}
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM4);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
}
virtual bool Serialize(FArchive& Ar) override
{
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
Ar << GBufferATextureVal << GBufferATextureSampler << DepthTextureVal << DepthTextureSampler << SceneTextureVal << SceneTextureSampler << CustomDataTextureVal << CustomDataTextureSampler;
return bShaderHasOutdatedParameters;
}
void SetRenderAssets(FRHICommandListImmediate& RHICmdList, FSceneRenderTargets& SceneContext,const FViewInfo& View)
{
SetTextureParameter(
RHICmdList,
GetPixelShader(),
GBufferATextureVal,
GBufferATextureSampler,
TStaticSamplerState<SF_Trilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI(),
SceneContext.GetGBufferATexture()
);
SetTextureParameter(
RHICmdList,
GetPixelShader(),
DepthTextureVal,
DepthTextureSampler,
TStaticSamplerState<SF_Trilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI(),
SceneContext.GetSceneDepthSurface()
);
SetTextureParameter(
RHICmdList,
GetPixelShader(),
SceneTextureVal,
SceneTextureSampler,
TStaticSamplerState<SF_Trilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI(),
SceneContext.GetSceneColorSurface()
);
SetTextureParameter(
RHICmdList,
GetPixelShader(),
CustomDataTextureVal,
CustomDataTextureSampler,
TStaticSamplerState<SF_Trilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI(),
SceneContext.GetGBufferDTexture()
);
SetUniformBufferParameter(
RHICmdList,
GetPixelShader(),
GetUniformBufferParameter<FViewUniformShaderParameters>(),
View.ViewUniformBuffer
);
//SetUniformBufferParameter(
// RHICmdList,
// GetPixelShader(),
// GetUniformBufferParameter<FViewUniformShaderParameters>(),
// GIdentityPrimitiveUniformBuffer.GetUniformBufferRHI()
//);
}
private:
FShaderResourceParameter GBufferATextureVal;
FShaderResourceParameter GBufferATextureSampler;
FShaderResourceParameter DepthTextureVal;
FShaderResourceParameter DepthTextureSampler;
FShaderResourceParameter SceneTextureVal;
FShaderResourceParameter SceneTextureSampler;
FShaderResourceParameter CustomDataTextureVal;
FShaderResourceParameter CustomDataTextureSampler;
};
IMPLEMENT_SHADER_TYPE(, FToonOutLinePS, TEXT("/Engine/Private/ToonOutline.usf"), TEXT("MainPS"), SF_Pixel);
struct FToonOutLineVertex
{
FVector4 Position;
FVector2D UV;
};
class FToonOutLineVertexDesc : public FRenderResource
{
public:
FVertexDeclarationRHIRef VertexDeclarationRHI;
// Destructor
virtual ~FToonOutLineVertexDesc() {}
virtual void InitRHI() override
{
FVertexDeclarationElementList Elements;
uint32 Stride = sizeof(FToonOutLineVertex);
Elements.Add(FVertexElement(0, STRUCT_OFFSET(FToonOutLineVertex, Position), VET_Float4, 0, Stride));
Elements.Add(FVertexElement(0, STRUCT_OFFSET(FToonOutLineVertex, UV), VET_Float2, 1, Stride));
VertexDeclarationRHI = RHICreateVertexDeclaration(Elements);
}
virtual void ReleaseRHI() override
{
VertexDeclarationRHI.SafeRelease();
}
};
TGlobalResource<FToonOutLineVertexDesc> GToonOutLineVertexDesc;
void FDeferredShadingSceneRenderer::RenderToonOutLine(FRHICommandListImmediate& RHICmdList)
{
// Draw grid.
//uint32 PrimitiveCount = 2;
//RHICmdList.DrawPrimitive(PT_TriangleList, 0, PrimitiveCount, 1);
FToonOutLineVertex Vertices[4];
Vertices[0].Position.Set(-1.0f, 1.0f, 0, 1.0f);
Vertices[1].Position.Set(1.0f, 1.0f, 0, 1.0f);
Vertices[2].Position.Set(-1.0f, -1.0f, 0, 1.0f);
Vertices[3].Position.Set(1.0f, -1.0f, 0, 1.0f);
Vertices[0].UV = FVector2D(0.0f, 0.0f);
Vertices[1].UV = FVector2D(1.0f, 0.0f);
Vertices[2].UV = FVector2D(0.0f, 1.0f);
Vertices[3].UV = FVector2D(1.0f, 1.0f);
static const uint16 Indices[6] =
{
0, 1, 2,
2, 1, 3
};
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
SceneContext.BeginRenderingSceneColor(RHICmdList, ESimpleRenderTargetMode::EExistingColorAndDepth, FExclusiveDepthStencil::DepthRead_StencilWrite, true);
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
if (View.IsPerspectiveProjection() == false)
{
continue;
}
TShaderMapRef<FToonOutLineVS> VertexShader(View.ShaderMap);
TShaderMapRef<FToonOutLinePS> PixelShader(View.ShaderMap);
RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f);
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
//GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGB, BO_Add, BF_One, BF_SourceAlpha>::GetRHI();
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGB, BO_Min, BF_One, BF_SourceAlpha>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GToonOutLineVertexDesc.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*VertexShader);
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(*PixelShader);
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);
PixelShader->SetRenderAssets(RHICmdList, SceneContext, View);
// Draw a quad covering the view.
DrawIndexedPrimitiveUP(
RHICmdList,
PT_TriangleList,
0,
ARRAY_COUNT(Vertices),
2,
Indices,
sizeof(Indices[0]),
Vertices,
sizeof(Vertices[0])
);
}
}
DeferredShadingRender.h
![v2-875b1d4042ddd2dbf08b5e140637b26c_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/be16172ba88a4b16a6df491a62624b5d.jpeg)
DeferredShadingRenderer.cpp
![v2-fe89dbff2b5c35c000eea37b2923c36b_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/63b642b66830fb74e2affd09e07ef700.jpeg)
BasePassCommon.ush
![v2-1cb1c32e89633fa5426adacddbf62508_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/c96b9e35d5bd7259d96d6576b6c8d37b.png)
ShadingModeMaterial.ush
![v2-25417f829107b1bed4f641469def38f4_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/f82c8f54733731d6e2213b288e73fc1b.jpeg)
Material.cpp的bool UMaterial::IsPropertyActive(EMaterialProperty InProperty) const 函数
![v2-2fb3e5b2049cb659dd39909e6ffb6e18_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/4933f50163b6ef1324b226a145b4a328.png)
MaterialGraph.cpp
![v2-cc11b3def44596b8e0d98b6f1b5e8852_b.jpg](https://i-blog.csdnimg.cn/blog_migrate/1a6225780cfb2896f3d587e5060cbe7e.jpeg)
Enjoy it!!!