一、图片投影
1、创建材质函数
MF_PS_Style_Shadow
定义 function input。
公开到库(可选)
Shadow代码:
/**
PS图层样式投影效果@param {UVs} texture coordinate@param {Texture} texture object@param {TextureSize} 纹理大小(x, y)@param {ShadowRGBA} 投影颜色与不透明度@param {ShadowRotate} 投影角度@param {ShadowLength} 投影距离@param {ShadowSize} 投影大小@param {BorderThreshold} 边界UVs阈值(左, 上, 右, 下)
*/
float4 Shadow(float2 UVs, Texture2D Texture, float2 TextureSize, float4 ShadowRGBA, float ShadowRotate, half ShadowLength, half ShadowSize, float4 BorderThreshold=0.001) {const float PI = acos(-1);// 单位像素float2 TexturePixel = 1 / TextureSize;// 角度float Angle = 360 * ShadowRotate;// 弧度float Degrees = Angle / 180 * PI;// 阴影反向方位(单位向量)float2 Direction = TexturePixel * float2(cos(Degrees), sin(Degrees));class Function {Texture2D Texture;SamplerState TextureSampler;float4 ShadowRGBA;float2 Position;float BorderThresholdLeft;float BorderThresholdTop;float BorderThresholdRight;float BorderThresholdBottom;float PI;float2 TexturePixel;// 阴影颜色float3 ShadowColor(float3 Color) {// 如果需要与颜色混合,在此修改返回值,如下式 正片叠底。// return this.ShadowRGBA.rgb * Color;return this.ShadowRGBA.rgb;}// 混合float3 Blend(float3 base, float3 blend, float alpha) {// 如果使用了混合模式,把 blend 按混合公式计算一次,如下式 正片叠底。// blend = base * blend;return lerp(base, blend, alpha);}// 纹理采样float4 TextureSample(float2 UVs) {// 如果需要 alpha 通道反向,在此修改。return Texture2DSampleLevel(this.Texture, this.TextureSampler, UVs, 0).xyzw;}float4 GetShadowRGBA(float2 UVs) {// 当前像素点 RGBAfloat4 TextureRGBA = this.TextureSample(UVs);// 阴影反向方位 UVsfloat2 PositionUVs = UVs + this.Position;// 阴影反向方位 UVs 超出了 0 - 1 的范围则不计算if (PositionUVs.x < this.BorderThresholdLeft || PositionUVs.x > this.BorderThresholdRight || PositionUVs.y < this.BorderThresholdTop || PositionUVs.y > this.BorderThresholdBottom) {return TextureRGBA;}// 阴影反向方位像素点RGBAfloat4 PositionRGBA = this.TextureSample(PositionUVs);// 阴影透明度float ShadowOpacity = PositionRGBA.a * this.ShadowRGBA.a;if (ShadowOpacity <= 0) {return TextureRGBA;}// 当前像素点混合后的结果色// this.ShadowRGBA.rgb 为 base 固有色// TextureRGBA.rgb 为 blend 固有色// TextureRGBA.a 为 alphafloat3 ShadowBlendColor = this.Blend(this.ShadowColor(PositionRGBA.rgb) * ShadowOpacity, TextureRGBA.rgb, TextureRGBA.a);// 当前像素点混合后的透明度float ShadowBlendOpacity = ShadowOpacity + TextureRGBA.a - ShadowOpacity * TextureRGBA.a;// 当前像素点混合后的RGBAreturn float4(ShadowBlendColor / ShadowBlendOpacity, ShadowBlendOpacity);}float Calculate1DGaussian(float x) {return exp(-0.5 * pow(this.PI * x, 2));}float4 GetShadowSizeRGBA(float2 UVs, half ShadowSize) {// 当前像素点 RGBAfloat4 TextureRGBA = this.TextureSample(UVs);// 投影大小范围内像素颜色累加float4 RGBASum = float4(0, 0, 0, 0);// 投影大小范围内像素的权重float WeightSum = 0;for (half x = -ShadowSize; x <= ShadowSize; x++) {for (half y = -ShadowSize; y <= ShadowSize; y++) {float Weight = this.Calculate1DGaussian(x / ShadowSize) * this.Calculate1DGaussian(y / ShadowSize);WeightSum += Weight;// 阴影偏移 UVsfloat2 OffsetUVs = UVs + float2(x, y) * this.TexturePixel + this.Position;if (OffsetUVs.x < this.BorderThresholdLeft || OffsetUVs.x > this.BorderThresholdRight || OffsetUVs.y < this.BorderThresholdTop || OffsetUVs.y > this.BorderThresholdBottom) {continue;}// 阴影偏移像素点 RGBAfloat4 OffsetRGBA = this.TextureSample(OffsetUVs);// 阴影透明度float Opacity = this.ShadowRGBA.a * OffsetRGBA.a;if (Opacity <= 0) {continue;}// 阴影结果色float4 RGBA = float4(this.ShadowColor(OffsetRGBA.rgb), Opacity);RGBASum += RGBA * Weight;}}// 模糊后的 RGBAfloat4 BlurRGBA = RGBASum / WeightSum;// 当前像素点混合后的结果色float3 Color = this.Blend(BlurRGBA.rgb * BlurRGBA.a, TextureRGBA.rgb, TextureRGBA.a);// 当前像素点混合后的透明度float Opacity = BlurRGBA.a + TextureRGBA.a - BlurRGBA.a * TextureRGBA.a;// 当前像素点混合后的RGBAreturn float4(Color / Opacity, Opacity);}}; // 注意要加分号// Function func;// func.Texture = Texture;// func.TextureSampler = TextureSampler;// func.ShadowRGBA = ShadowRGBA;// func.Position = ShadowLength * Direction;// func.BorderThresholdLeft = BorderThreshold.x;// func.BorderThresholdTop = BorderThreshold.y;// func.BorderThresholdRight = 1 - BorderThreshold.z;// func.BorderThresholdBottom = 1 - BorderThreshold.w;// func.PI = PI;// func.TexturePixel = TexturePixel;Function func = { Texture, TextureSampler, ShadowRGBA, ShadowLength * Direction, BorderThreshold.x, BorderThreshold.y, 1 - BorderThreshold.z, 1 - BorderThreshold.w, PI, TexturePixel };if (ShadowSize < 1) {return func.GetShadowRGBA(UVs);}return func.GetShadowSizeRGBA(UVs, round(ShadowSize));
}
2、创建材质
M_PS_Style_Shadow
修改 材质域 和 混合模式。
调用之前创建的材质函数。
材质函数调用方式。
如果之前没有公开到库,则使用 material function call 调用
3、效果预览
二、文字投影
1、添加 Retainer Box
设置尺寸,并指定效果材质。
2、添加 TextBlock
3、设置材质实例参数
由于字体所在画布大小为 300 * 100,这里要设置同步。
4、效果预览(需要运行游戏才能看到效果)
ShadowSize 为 0。
ShadowSize 为 10。
ShadowSize 为 20。
需要改颜色就修改 ShadowRGBA。
三、人物角色投影
1、创建渲染目标
命名 RT_Equipment。
用于 UI 贴图,修改参数,这里大小使用1024。
2、创建 Actor
命名 BP_Equipment
添加 场景捕获组件2D。
根据需要设置参数。
添加 骨骼网络体组件。
指定模型与动画。
调整好位置。
3、放入场景。
将 BP_Equipment 放入 Level,也可以在运行时动态生成。
4、修改材质
由于 A 通道需要反向,所以之前的 Shadow 代码做如下改动:
// 纹理采样float4 TextureSample(float2 UVs) {// 如果需要 alpha 通道反向,在此修改。float4 PixelRGBA = Texture2DSampleLevel(this.Texture, this.TextureSampler, UVs, 0).xyzw;return float4(PixelRGBA.rgb, 1 - PixelRGBA.a);}
5、效果预览
四、使用混合模式
假定使用 正片叠底,代码做如下改动。
// 阴影颜色float3 ShadowColor(float3 Color) {// 如果需要与颜色混合,在此修改返回值,如下式 正片叠底。return this.ShadowRGBA.rgb * Color;}
也可以换成其他的混合模式,具体算法可以参考文章 《HLSL实现PS混合模式》。