上一篇傳送門:
https://blog.csdn.net/qq_27534999/article/details/100985558
上一篇講到可以用 Unity 插件刷頂點色,但可能美術還是更傾向于在 3dsMax 之類的軟體裡處理。
好在這也是有辦法的,那就是在 3dsMax 裡加載自定義 Shader。廢話不多說,現在講下步驟(以 3dsMax 2018 為準):
1、打開材質編輯器,一般在右上角。
(若打開按鈕圖示不一樣則長按滑鼠左鍵有下拉菜單)
2、在材質編輯器中選擇 DirectX Shader
3、在 DirectX 明暗器中選擇 HLSL 檔案加載即可,字尾是 .fx,HLSL 和 Cg 的文法差别不大,一般 3dsMax會自帶一些 .fx 檔案,例如 StandardFX11.fx 之類,聰明的你打開後模仿着重寫一下 Shader 即可~ (文章末尾會附贈修改後的 Shader)
4、選中你想要的的 .fx 檔案後,将材質球拖到模型上。
(這裡注意參數要指定一個平行光 Direct001,否則光照方向是預設從鏡頭發射,會很奇怪)
5、工具->指定頂點顔色,再選中球體,這樣就可以在 3dsMax 裡刷頂點色了,邊刷邊看,所見即所得,真是 HIGH 到不行啊~!
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
參考資料:
無參考資料