虚幻4渲染编程(Shader篇)【第九卷:自定义渲染管线】

先上我的专栏的目录:

小IVan:专题概述及目录​zhuanlan.zhihu.com图标

然后再按老规矩先上效果:

v2-e04f87c6b1ca8d5192d1c813162b27c3_b.jpg

什么嘛就是一堆绿色嘛!但是这个绿色的来源是我们自己定义的一个pass!

v2-8cbf16ba95935f3a9f6903ef73b4236e_b.jpg
v2-a93a47774653a8bc4e87fbd826fc323a_b.jpg

我在雾气渲染之后再渲染了一次我自己的pass,当然你也可以渲染些其他的东西。这一节旨在探索如何自定义虚幻的渲染管线。

下面就让我们开始吧!

首先我们找到Render/Private/DeferredShadingRender.h和对应的DeferredShadingRender.cpp这两文件。我们能在头文件中找到众多Render函数。对没错这个就是我们虚幻的渲染管线。很多人又是翻文档,又是抓帧地捋虚幻的渲染管线,真正的渲染这些东西的就是这里啦。

v2-b31393f4a571bed56643b81f00ba7b4d_b.jpg

我这里只截了一笑部分,还有很多很多函数。

下面打开DeferredShadingRender.cpp我们找到render函数

v2-424b0b93d8f5448caf007ef43ce57e9c_b.jpg

这里就是虚幻tiled延迟光照渲染管线下每一帧绘制的地方了。找到地方之后我们开始加入我们的“绿啦绿啦!”自定义pass。

在DeferredShadingRender.h中加入如下代码:

v2-c6226194128340547c217c28b344107e_b.jpg

然后在DeferredShadingRender.cpp中调用它

v2-60d0866259e8f9c388434fdef7af5bcb_b.jpg

我选择在雾气之后渲染我的pass,当然你可以把它放到其他地方,不过需要注意的是,如果你放的位置不对你是拿不到有些数据的,因为那时候可能那些数据还没绘制出来。

然后我们自己建个MyPass.h文件和MyPass.cpp文件还有MyPass.usf文件然后generate一下然后VS重载下项目。然后我们就有以下几个文件了:

v2-ab293695485d8b898c7b20be3c50c765_b.jpg

首先打开MyPass.cpp敲出我们的代码框架(注意看注释)

#include "MyPass.h"
#include "DeferredShadingRenderer.h"
#include "AtmosphereRendering.h"
#include "ScenePrivate.h"
#include "Engine/TextureCube.h"
#include "PipelineStateCache.h"

DECLARE_GPU_STAT(MyPass);
//顶点着色器
class FMyPassVS : public FGlobalShader
{
	DECLARE_SHADER_TYPE(FMyPassVS, Global);
};
//实例化顶点着色器
IMPLEMENT_SHADER_TYPE(, FMyPassVS, TEXT("/Engine/Private/MyPass.usf"), TEXT("MainVS"), SF_Vertex);
//像素着色器
class FMyPassPS : public FGlobalShader
{
	DECLARE_SHADER_TYPE(FMyPassPS, Global);

};
//全局实例化像素着色器
IMPLEMENT_SHADER_TYPE(, FMyPassPS, TEXT("/Engine/Private/MyPass.usf"), TEXT("MainPS"), SF_Pixel);

//顶点数据结构体
struct FMyPassVertex
{
	FVector4	Position;
	FVector2D	UV;
};

//顶点输入布局
class FMyPassVertexDec : public FRenderResource
{
};
//顶点输入布局的全局实例
TGlobalResource<FMyPassVertexDec> GMyPassVertexDeclaration;

//绘制函数
bool FDeferredShadingSceneRenderer::RenderMyPass(FRHICommandListImmediate& RHICmdList)
{

}

有了代码框架后我们就开始完善我们的代码了。

首先是顶点着色器

v2-1ad27df34b4cfd438ab1c6c83d6d87a9_b.jpg

看过我前面文章的话这个顶点着色器简直就是小case。

下面是像素着色器:

v2-57d5e262a84d940a1826c2fdc8e7af20_b.jpg

还是很简单的,因为本例只是输出个颜色,所以什么复杂逻辑都没有的。

然后是顶点输入布局

v2-c91684a87a6bfbd7690091ca920915fc_b.jpg

最后是渲染函数:

bool FDeferredShadingSceneRenderer::RenderMyPass(FRHICommandListImmediate& RHICmdList)
{
	// Draw grid.
	//uint32 PrimitiveCount = 2;
	//RHICmdList.DrawPrimitive(PT_TriangleList, 0, PrimitiveCount, 1);
	FMyPassVertex 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, 1.0f);
	Vertices[1].UV = FVector2D(1.0f, 1.0f);
	Vertices[2].UV = FVector2D(0.0f, 0.0f);
	Vertices[3].UV = FVector2D(1.0f, 0.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<FMyPassVS> VertexShader(View.ShaderMap);
		TShaderMapRef<FMyPassPS> 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.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
		GraphicsPSOInit.PrimitiveType = PT_TriangleList;

		GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GMyPassVertexDeclaration.VertexDeclarationRHI;
		GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*VertexShader);
		GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(*PixelShader);

		SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);

		// Draw a quad covering the view.
		DrawIndexedPrimitiveUP(
			RHICmdList,
			PT_TriangleList,
			0,
			ARRAY_COUNT(Vertices),
			2,
			Indices,
			sizeof(Indices[0]),
			Vertices,
			sizeof(Vertices[0])
		);

	}

	return true;
}

这里需要注意的是几点:

(1)RenderTarget我们是从Scene中拿到的

v2-5b560dd8d84039b0deadfdff5f35de31_b.jpg

SceneRender会管理一帧画面的RT。然后就是这个View了

v2-a01466b85032d24b91926e04ab316174_b.jpg

因为游戏里可能会有多个视口的,这个View就是负责管理这些的,具体可以跟进去,注释很完善。我这里就不多说了。

最后就是我们的shader了:

v2-805cc69f052f931208a3ff6fa2bb6071_b.jpg

我这里直接输出了一个很简单的绿色。

至此我们完成自定义pass。

unity都出自定义管线了,会给开发者很大的方便。比如我想做个卡通风格的游戏,我就可以把勾线的pass自己定制一个,而不是放后期里,让勾线成为引擎本身具有的功能,你在模型编辑器特效编辑器里都能预览到自定义pass的渲染结果:

v2-783943c7d231fdab4a03acd31f057744_b.jpg

我这里只是随便渲染了个绿色。

因为Render函数里有大量可利用的数据,所以这里还能做很多文章,我这里只是举个栗子。

Enjoy it!!!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cpongo11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值