3D-MAX之Shader工具(描边)

本文详细介绍了如何在3Ds-Max中开发DX着色器,特别是针对卡通渲染中的描边效果。通过理解3Ds-Max的工具和属性,如顶点色的使用,以及利用HLSL开发描边着色器,包括InvertedHull算法,实现了不受远近影响的描边宽度和内描边控制。同时,文章提供了关键代码片段和着色器模板参考。
摘要由CSDN通过智能技术生成

3D-MAX之Shader工具(描边)

零、前言

本文将主要介绍如何在 3Ds-Max 中开发DX着色器,来满足美术在编辑模型过程中可以直接浏览效果的需求。

如笔者最近在做卡通渲染的项目,卡通渲染中角色的描边通常需要使用到顶点色。

那么就需要制作一些工具(这里是Shader),方便美术在刷的过程中可以实时地观察到效果,进而提高工作效率。

一、3Ds-Max工具简单认识

1.1 创建面板

点击后,在视口窗口拖动即可创建。

1.2 快捷器

视图窗口的快捷键

Z     		: 将所选物体作为中心来观察,相当于UE中的F
F3    		: 线框模式
F4    		:Shader模型下显示线框
G     		:隐藏网格
Q,W,E,R    : 对物体的操作
M        	 :材质管理器

1.3 如何查看其它属性(如法线)

  1. 选中物体;
  2. ModifyList选择EditorNormal;

结果如下:

Edit Poly -> Polygeon:Smoothing Groups 可以平滑?修改法线。

二、3Ds-Max刷顶点色

由于笔者是程序,关于如何使用 3ds-max 刷顶点色也摸索了一会儿。

幸而看到了一下这篇文章得到了正确的方法。3ds Max中顶点色RGBA的绘制 操作如下:

  1. 要将网格设置为 Poly模式
  2. 选择 工具 -> Assign Vertex Colors -> ModifierList -> 选择VertexPaint ;
  3. 分层绘制RGBA ;

在max中正确的绘制顶点色的方式应该是 分通道来绘制

  • 一个VertexPaint修改器,对应一个通道。
  • 绘制RGB通道其中之一时,在Mode模式中选择Add模式,并确认通道为顶点颜色Vertex Color
  • 需要绘制顶点色的Alpha通道时,确认Mode模式为Normal,还需要注意将通道改为Vertex Alpha方式。

三、描边着色器开发

3.1 DX着色器

3ds-Max 中支持DX着色器HLSL的开发。

按照惯例,我们应该先看一下 Built-In 的着色器。

步骤如下:

  1. 打开材质面板
  2. 更改材质类型到DXShader

  1. 点击HLSLFile下可以打开路径,有很多模板。

Build-In 着色器路径:

C:\Program Files\Autodesk\3ds Max 2020\maps\fx

将一些着色器拿来使用,翻看一些着色器示例,其实就可以进行开发了。

在笔者实践过程中,发现有部分材质错误(无法使用)。在系统环境变量中添加 OGS_WARNING_LOG_ENABLED。值为1。

可以看到错误如下:

最后,以default10.fx和StandardFX11.fx可以使用的作为模板即可开发。

3.2 语义和写法

Supported HLSL Shader Semantics 提供了一些支持的HLSL着色器语义。

矩阵声明:

声明贴图和对应的SamplerState:

纹理采样:

矩阵Mul:

多Pass:

3.4 顶点色获取

3ds-Max DX着色器中,需要从自定义的Texcoord中获取。这里是从内置着色器中发现的。

#ifdef _MAX_
int texcoord1 : Texcoord
<
	int Texcoord = 1;
	int MapChannel = 0;
	string UIWidget = "None";
>;

int texcoord2 : Texcoord
<
	int Texcoord = 2;
	int MapChannel = -2;
	string UIWidget = "None";
>;

int texcoord3 : Texcoord
<
	int Texcoord = 3;
	int MapChannel = -1;
	string UIWidget = "None";
>;
#endif

其中,MapChannel可以通过 Tools工具栏的 Channel Info进行查看,如下图所示。

那么VS的输入数据就声明如下:

3.4 描边算法

描边算法采用的是Inverted Hull算法。支持多种途径(通常、顶点颜色、切线、UV)来控制描边。
(1)frontface culling 将几何体正面剔除。

  • 前向渲染可以轻松做到。在UE中要采用TwoSidedSign+Mask来实现。

(2)将顶点沿法线方向外扩一段距离。

  • 为了OutlineWidth不随远近变化。需要将OutlineWidth乘以ViewSpace的Z值。就是ClipSpace的w值。

(3)对于内部不需要描边的顶点,使用顶点色的某个通道,让像素深度增大一定值,使得描边被遮住。

描边Pass代码:

#ifdef _MAX_
int texcoord1 : Texcoord
<
	int Texcoord = 1;
	int MapChannel = 0;
	string UIWidget = "None";
>;

int texcoord2 : Texcoord
<
	int Texcoord = 2;
	int MapChannel = -2;
	string UIWidget = "None";
>;

int texcoord3 : Texcoord
<
	int Texcoord = 3;
	int MapChannel = -1;
	string UIWidget = "None";
>;
#endif

/* data from application vertex buffer */
struct appdata {
	float4 Position		: POSITION;
	float3 Normal		: NORMAL;
	float3 Tangent		: TANGENT;
	float3 Binormal		: BINORMAL;
	float2 UV0			: TEXCOORD0;	
	float3 Colour		: TEXCOORD1;
	float3 Alpha		: TEXCOORD2;
	float3 Illum		: TEXCOORD3;
	float3 UV1		: TEXCOORD4;
	float3 UV2		: TEXCOORD5;
	float3 UV3		: TEXCOORD6;
	float3 UV4		: TEXCOORD7;
};

struct VS_OUTPUT1
{
    float4 Pos       : SV_POSITION;
    float3 Normal    : NORMAL;
	float4 color     : COLOR;
};

/ OutlinePass /
// parameters
float OutlineWidth<
	string UIName = "Outline Width";
	string UIWidget = "slider";
	float UIMin = 0.01f;
	float UIMax = 100.0f;	
>  = 0.24f;

float4 OutLineColor  <
	string UIName = "OutLine Color";
	string UIWidget = "Color";
> = float4( 0.0f, 0.0f, 0.0f, 1.0f );

VS_OUTPUT1 VS1(appdata IN)
{
    VS_OUTPUT1 Out = (VS_OUTPUT1)0;

    // 顶点色R控制描边
	// Alpha通道(W) 控制描边的宽度,默认值为1,值越小描边越细。
	// B通道(Z)控制深度偏移,默认为1,值越小内描边可以逐渐隐藏。
	float VertexColor_Width = IN.Alpha.x;
	//float VertexColor_PixelDepth = IN.Colour.b;

	// 像素深度偏移
	float4 WorldPos = mul(IN.Position,World);
	//float3 WorldViewDir = normalize(ViewInverse[3].xyz-WorldPos.xyz);
	//IN.Pos += -WorldViewDir * VertexColor_PixelDepth * PixelDepth * VertexColor_PixelDepth; 

    //float4 Pos = mul(float4(IN.Pos,1),WorldViewProj);
	float4 ViewPos = mul(WorldPos,View);
	float4 Pos = mul(ViewPos,Projection);

	// 为了让勾边与物体距离相机远近无关,描边粗细保持不变
    float3 ViewNormal = mul(IN.Normal,(float3x3)WorldViewInverseTranspose);
    // 乘以Pos.w,将法线变换到NDC空间
    float3 NDCNormal = normalize(mul(float4(ViewNormal,1),Projection)) * Pos.w;
    // 将近裁剪面右上角位置的顶点变换到观察空间
    float4 NearUpperRight = mul(float4(1,1,0,1),ProjInverse);
    // 求得屏幕宽高比
    float Aspect = abs(NearUpperRight.y / NearUpperRight.x);
    NDCNormal.x *= Aspect;
    Pos.xy += NDCNormal * OutlineWidth * 0.1f * IN.Alpha.x;
    Out.Pos = Pos;

    return Out;
}

float4 PS1(VS_OUTPUT1 IN) : COLOR
{   
    float4 Color;
	Color =  OutLineColor;
    return Color;
}

/ RasterizerStateS /
RasterizerState RS_CullBack
{
    //FillMode = WIREFRAME; 
    CullMode = Back;
};

RasterizerState RS_CullFront
{
    CullMode = Front;
};

/ TECHNIQUES /
fxgroup dx11
{
technique11 Main_11 <string Script = "Pass=p0;Pass=p1";>
{
	pass p0 <string Script = "Draw=geometry;";> 
    {
		SetRasterizerState(RS_CullBack);
         SetVertexShader(CompileShader(vs_5_0,std_VS()));
         SetGeometryShader( NULL );
		SetPixelShader(CompileShader(ps_5_0,std_PS()));
    }
	pass p1 <string Script = "Draw=geometry;";> 
    {
        SetRasterizerState(RS_CullFront);
        SetVertexShader(CompileShader(vs_5_0,VS1()));
        SetGeometryShader( NULL );
	    SetPixelShader(CompileShader(ps_5_0,PS1()));
    }
}
}

参考文献

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值