天天看點

Unity Shader 卡通渲染 (四):3dsMax 中加載自定義 Shader 刷頂點色

上一篇傳送門:

https://blog.csdn.net/qq_27534999/article/details/100985558

上一篇講到可以用 Unity 插件刷頂點色,但可能美術還是更傾向于在 3dsMax 之類的軟體裡處理。

好在這也是有辦法的,那就是在 3dsMax 裡加載自定義 Shader。廢話不多說,現在講下步驟(以 3dsMax 2018 為準):

1、打開材質編輯器,一般在右上角。

(若打開按鈕圖示不一樣則長按滑鼠左鍵有下拉菜單)

Unity Shader 卡通渲染 (四):3dsMax 中加載自定義 Shader 刷頂點色

2、在材質編輯器中選擇 DirectX Shader

Unity Shader 卡通渲染 (四):3dsMax 中加載自定義 Shader 刷頂點色

3、在 DirectX 明暗器中選擇 HLSL 檔案加載即可,字尾是 .fx,HLSL 和 Cg 的文法差别不大,一般 3dsMax會自帶一些 .fx 檔案,例如 StandardFX11.fx 之類,聰明的你打開後模仿着重寫一下 Shader 即可~ (文章末尾會附贈修改後的 Shader)

Unity Shader 卡通渲染 (四):3dsMax 中加載自定義 Shader 刷頂點色

4、選中你想要的的 .fx 檔案後,将材質球拖到模型上。

(這裡注意參數要指定一個平行光 Direct001,否則光照方向是預設從鏡頭發射,會很奇怪)

Unity Shader 卡通渲染 (四):3dsMax 中加載自定義 Shader 刷頂點色

5、工具->指定頂點顔色,再選中球體,這樣就可以在 3dsMax 裡刷頂點色了,邊刷邊看,所見即所得,真是 HIGH 到不行啊~!

Unity Shader 卡通渲染 (四):3dsMax 中加載自定義 Shader 刷頂點色

6、然後是修改後的 Shader 檔案(字尾 .fx),用的是上一篇仿塞爾達的 Shader。可以發現文法大緻都是相同的,聰明的你肯定一看就會啦~!

// 在StandardFX11.fx的基礎上修改
string ParamID = "0x003";

float Script : STANDARDSGLOBAL <
    string UIWidget = "none";
    string ScriptClass = "object";
    string ScriptOrder = "standard";
    string ScriptOutput = "color";
    string Script = "Technique=Main;";
> = 0.8;

 UN-TWEAKABLES - AUTOMATICALLY-TRACKED TRANSFORMS 

float4x4 WorldITXf : WorldInverseTranspose < string UIWidget="None"; >;
float4x4 WvpXf : WorldViewProjection < string UIWidget="None"; >;
float4x4 WorldXf : World < string UIWidget="None"; >;
float4x4 ViewIXf : ViewInverse < string UIWidget="None"; >;

#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

 TWEAKABLE PARAMETERS 
// 面闆參數
/// Point Lamp 0 
float3 Lamp0Pos : POSITION <
    string Object = "PointLight0";
    string UIName =  "Light Position";
    string Space = "World";
	int refID = 0;
> = {-0.5f,2.0f,1.25f};

#ifdef _MAX_
float3 Lamp0Color : LIGHTCOLOR
<
	int LightRef = 0;
	string UIWidget = "None";
> = float3(1.0f, 1.0f, 1.0f);
#else
float3 Lamp0Color : Specular <
    string UIName =  "Lamp 0";
    string Object = "Pointlight0";
    string UIWidget = "Color";
> = {1.0f,1.0f,1.0f};
#endif

float4 _Color  <
	string UIName = "Color";
	string UIWidget = "Color";
> = float4( 0.0f, 0.0f, 0.0f, 1.0f );    // testColor

 COLOR & TEXTURE /

Texture2D <float4> g_TopTexture : DiffuseMap< 
	string UIName = "Top Diffuse";
	string ResourceType = "2D";
	int Texcoord = 0;
	int MapChannel = 1;
>;

// 結構體
/* 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;
};

/* data passed from vertex shader to pixel shader */
struct vertexOutput {
    float4 HPosition	: SV_Position;
    float4 UV0		: TEXCOORD0;
    // The following values are passed in "World" coordinates since
    //   it tends to be the most flexible and easy for handling
    //   reflections, sky lighting, and other "global" effects.
    float3 LightVec	: TEXCOORD1;
    float3 WorldNormal	: TEXCOORD2;
    float3 WorldTangent	: TEXCOORD3;
    float3 WorldBinormal : TEXCOORD4;
    float3 WorldView	: TEXCOORD5;
	float4 UV1		: TEXCOORD6;
	float4 UV2		: TEXCOORD7;
	float4 wPos		: TEXCOORD8;
};
 
/ VERTEX SHADING /
// 頂點着色器
/*********** Generic Vertex Shader ******/

vertexOutput std_VS(appdata IN) {
    vertexOutput OUT = (vertexOutput)0;
    OUT.WorldNormal = mul(IN.Normal,WorldITXf).xyz;
    OUT.WorldTangent = mul(IN.Tangent,WorldITXf).xyz;
    OUT.WorldBinormal = mul(IN.Binormal,WorldITXf).xyz;
    float4 Po = float4(IN.Position.xyz,1);
    float3 Pw = mul(Po,WorldXf).xyz;
    OUT.LightVec = (Lamp0Pos - Pw);
    OUT.WorldView = normalize(ViewIXf[3].xyz - Pw);
    OUT.HPosition = mul(Po,WvpXf);
	OUT.wPos = mul(IN.Position, WorldXf);
	
// UV bindings
// Encode the color data
 	float4 colour;
   	colour.rgb = IN.Colour * IN.Illum;
   	colour.a = IN.Alpha.x;
   	OUT.UV0.z = colour.r;
   	OUT.UV0.a = colour.g;
  	OUT.UV1.z = colour.b;
   	OUT.UV1.a = colour.a;

// Pass through the UVs
	OUT.UV0.xy = IN.UV0.xy;
   	OUT.UV1.xy = IN.UV1.xy;
   	OUT.UV2.xyz = IN.UV2.xyz;
// 	OUT.UV3 = OUT.UV3;
// 	OUT.UV4 = OUT.UV4;
    return OUT;
}

/ PIXEL SHADING //
// 像素(片元)着色器
float4 std_PS(vertexOutput IN) : SV_Target {
    float3 diffContrib;
    float3 specContrib;
    float3 Ln = normalize(IN.LightVec);
    float3 Vn = normalize(IN.WorldView);
    float3 Nn = normalize(IN.WorldNormal);
    float3 Tn = normalize(IN.WorldTangent);
    float3 Bn = normalize(IN.WorldBinormal);
	float3 Hn = normalize(Ln+Vn);
	float4 vertColour = float4(IN.UV0.z,IN.UV0.w,IN.UV1.z,IN.UV1.w);	
	//float3 BottomCol = k_d.rgb; 
	
	//float4 _Color=float4(0.5,0.5,1,1);
	float4 _Specular=float4(0.5,0.5,0.5,1.0);
	float _SpecularScale=0.025;
	float _ShadowThreshold=0.5;
	float _ShadowBrightness=0.5;
	float4 _LightColor0=float4(1,1,1,1);
	float _RimThreshold=0.5;
	float4 _RimColor=float4(1,1,1,1);
	float _RimPower=4;

	float3 worldNormal = Nn; //法線 N
	float3 worldLightDir = Ln; //光照方向 L
	float3 worldViewDir = Vn; //視角方向 V
	float3 worldHalfDir = Hn; //高光計算用

	// sample the texture
	//初始顔色
	float4 col = float4(0.5,0.5,0.5,1); 
	float spec = dot(worldNormal, worldHalfDir)+(vertColour.g-0.5)*2;
	// w值也可用一個較小的值代替,效果差别不大
	float w = fwidth(spec)*2.0;
	float4 specular = _Specular * lerp(0,1,smoothstep(-w, w, spec+_SpecularScale-1)) * step(0.001, _SpecularScale);
	float diffValue = dot(worldNormal, worldLightDir)+(vertColour.r-0.5)*4;
	float diffStep = smoothstep(-w+_ShadowThreshold, w+_ShadowThreshold, diffValue);
	float4 light = _LightColor0 * 0.5 + 0.5;
	float4 diffuse = light * col * (diffStep + (1 - diffStep) * _ShadowBrightness) * _Color;	
	float rimValue = pow(1 - dot(worldNormal, worldViewDir)+(vertColour.b-0.5)*2, _RimPower);
	float rimStep = smoothstep(-w+_RimThreshold, w+_RimThreshold, rimValue);
	
	float4 rim = light * rimStep * 0.5 * diffStep * _RimColor;
	float4 final = diffuse + rim + specular;

	return float4(final.rgb,1.0);
}

/ TECHNIQUES /
fxgroup dx11
{
technique11 Main_11 <
	string Script = "Pass=p0;";
> {
	pass p0 <
	string Script = "Draw=geometry;";
    > 
    {
        SetVertexShader(CompileShader(vs_5_0,std_VS()));
        SetGeometryShader( NULL );
		SetPixelShader(CompileShader(ps_5_0,std_PS()));
    }
}
}
fxgroup dx10
{
technique10 Main_10 <
	string Script = "Pass=p0;";
> {
	pass p0 <
	string Script = "Draw=geometry;";
    > 
    {
        SetVertexShader(CompileShader(vs_4_0,std_VS()));
        SetGeometryShader( NULL );
		SetPixelShader(CompileShader(ps_4_0,std_PS()));
    }
}
}
/// eof //
           

謝謝觀賞~!

下一篇傳送門:

https://blog.csdn.net/qq_27534999/article/details/102581563

參考資料:

無參考資料

繼續閱讀