概述
前言
提示:这里可以添加本文要记录的大概内容:
本次分享主要为水瓶效果,思路借鉴于https://www.patreon.com/posts/quick-game-art-18245226 该链接,不过部分内容较难理解,所以打算使用自己的思路实现一下
提示:以下是本篇文章正文内容,下面案例可供参考
一、实现效果
效果分析
1.胶囊体模型,可修改shader属性修改水瓶水面高度
2.水面厚度,可修改shader属性修改水平厚度
3.水瓶面颜色,可修改shader属性修改水平面颜色
4.水瓶满足物理效果,可左右摇曳,上下反转
5.菲涅尔边缘效果,加强层级
6.边缘拓展,增加形体
实现流程
1.边缘拓展,增加形体
首先是边缘效果,为了突出水平的的形状,我们的一个Pass用于边缘效果实现。这里直接使用了顶点沿法线方向拓展,并且追加了菲尼尔效果,作为我们第一个Pass这里关闭深度写入,避免第二个Pass深度测试失败。以下是实现效果。
边缘效果代码如下:
```c
Pass
{
ZWrite Off
Cull Back
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float4 world_pos : TEXCOORD1 ;
float3 world_normal:TEXCOORD2 ;
};
float _EdgePower;
float _DimPow;
v2f vert (appdata v)
{
v2f o;
v.vertex.xyz += v.normal * _EdgePower;
o.vertex = UnityObjectToClipPos(v.vertex);
o.world_pos = mul(unity_ObjectToWorld, v.vertex);
o.world_normal = UnityObjectToWorldNormal(v.normal);
return o;
}
fixed4 frag (v2f i, fixed facing : VFACE ) : SV_Target
{
float3 world_view_dir = normalize(UnityWorldSpaceViewDir(i.world_pos));
float rim = 1 - pow(saturate(dot(i.world_normal, world_view_dir)), _DimPow);
return fixed4(1,1,1, rim);
}
ENDCG
}
胶囊体模型,可修改shader属性修改水瓶水面高度
由于我们的水平面要要适应水瓶旋转的各个角度保持平衡,所以我们这里需要使用世界空间来计算,又避免世界空间影响水平高度计算,我们需要将对象空间的中心点切换到世界空间,再用世界空间的中心位置减去世界空间的顶点位置,这样既不受世界空间的影响,也根据差值计算水平高度。得到的差值再减去一个控制变量,得到我们alpha值,我们就是通过修改透明通道的值来做剔除效果,最后Pass标记上当然要加上 AlphaToMask On 剔除透明部分。在上面的链接里面我看到他直接用object->world的矩阵乘以对象空间顶点的xyz,这里我是不太理解的,object->world矩阵不是一个4x4的矩阵,怎么能乘以一个3维坐标呢。而且算出来的坐标也不受世界空间影响,我是觉得很奇怪的,有谁知道可以跟我讲一下。
边缘效果代码如下:
fixed4 frag (v2f i, fixed facing : VFACE ) : SV_Target
{
float4 world_center = mul(unity_ObjectToWorld, float4(0, 0, 0, 1));
float4 world_pos = i.world_pos;
float4 cache_output1 = world_center - world_pos;
float cache_output2 = 1 - _Height;
float mulTime2 = _Time.y * _Speed;
float alpha = clamp((cache_output1.y - cache_output2 )/ _Falloff, 0 , 1 ) ;
return fixed4(_Color.rgb , alpha);
}
水面厚度,可修改shader属性修改水平厚度
水平厚度我们直接调用step()函数,根据以上计算的差值,我们再减去一个水平厚度控制变量,得到一个非0即1的结果,再根据这个结果在Color1,以及Color之间做渐变。step()和smoothstep()这个函数我觉得在做渐变时候很管用,一个是无过渡式渐变,一个是过渡式渐变。
边缘效果代码如下:
float4 world_center = mul(unity_ObjectToWorld, float4(0, 0, 0, 1));
float4 world_pos = i.world_pos;
float4 cache_output1 = world_center - world_pos;
float cache_output2 = 1 - _Height;
float alpha = clamp((cache_output1.y - cache_output2 )/ _Falloff, 0 , 1 ) ;
float cache_output3 = step(cache_output1.y - _Width, cache_output2);
float4 cache_output4 = lerp(_Color, _Color3, cache_output3);
return fixed4(cache_output4.rgb , alpha);
水瓶面颜色,可修改shader属性修改水平面颜色,菲涅尔边缘效果,加强层级
水平面颜色我们使用了fixed facing : VFACE 字段,该字段表示被渲染的面是否朝向摄像机,用于片段着色器。同时我们也追加菲尼尔效果
水瓶满足物理效果,可左右摇曳,上下反转
这个的实现方式我主要借鉴链接里的思路。首先左右摇曳,我们需要得到速度以及力的衰弱权重。我这里做了简化版。链接还考虑了旋转值,我这边就直接考量速度与衰弱。速度就直接拿上一帧的坐标减去当前帧的坐标得到速度,衰弱就根据时间系数在速度值与0之间做差值得到衰弱权重。并将其整合成_WobbleX,_WobbleZ两个方向上的权重传入shader计算。
以下是完整代码
shader部分
Shader "Unlit/Test14"
{
Properties
{
_Height("Height", Float) = 0.1
_Falloff("Falloff", Float) = 0.1
_Color("Color", Color) = (1, 0, 0, 0)
_Color2("Color", Color) = (0, 0, 0, 0)
_Color3("Color", Color) = (0, 1, 0, 0)
_Speed("Speed", Float) = 0
_Size("Size", Float) = 0.1
_EdgePower("EdgePower", Range(0, 1)) = 0.1
_DimPow("DimPower", Float) = 1
_RimColor("RimColor", Color) = (1, 1, 1, 1)
[HideInInspector] _WobbleX ("WobbleX", Range(-1,1)) = 0.0
[HideInInspector] _WobbleZ ("WobbleZ", Range(-1,1)) = 0.0
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent"}
LOD 100
Pass
{
ZWrite Off
Cull Back
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float4 world_pos : TEXCOORD1 ;
float3 world_normal:TEXCOORD2 ;
};
float _EdgePower;
float _DimPow;
v2f vert (appdata v)
{
v2f o;
v.vertex.xyz += v.normal * _EdgePower;
o.vertex = UnityObjectToClipPos(v.vertex);
o.world_pos = mul(unity_ObjectToWorld, v.vertex);
o.world_normal = UnityObjectToWorldNormal(v.normal);
return o;
}
fixed4 frag (v2f i, fixed facing : VFACE ) : SV_Target
{
float3 world_view_dir = normalize(UnityWorldSpaceViewDir(i.world_pos));
float rim = 1 - pow(saturate(dot(i.world_normal, world_view_dir)), _DimPow);
return fixed4(1,1,1, rim);
}
ENDCG
}
Pass
{
AlphaToMask On
Cull Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float4 world_pos : TEXCOORD1 ;
float3 world_normal: TEXCOORD2 ;
};
float _Height;
float _Falloff;
float4 _Color;
float4 _Color2;
float4 _Color3;
float _WobbleX, _WobbleZ;
float _Speed;
float4 _RimColor;
float _Size;
float _DimPow;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.world_pos = mul(unity_ObjectToWorld, v.vertex);
o.world_normal = UnityObjectToWorldNormal(v.normal);
return o;
}
fixed4 frag (v2f i, fixed facing : VFACE ) : SV_Target
{
float3 world_view_dir = normalize(UnityWorldSpaceViewDir(i.world_pos));
float rim = 1 - pow(saturate(dot(i.world_normal, world_view_dir)), _DimPow);
facing = facing * 0.5 + 0.5;
float4 rim_color = rim * _RimColor * facing;
float4 world_center = mul(unity_ObjectToWorld, float4(0, 0, 0, 1));
float4 world_pos = i.world_pos;
float4 cache_output1 = world_center - world_pos;
float cache_output2 = 1 - _Height;
float mulTime2 = _Time.y * _Speed;
float cache_output_3 = (( _WobbleX * cache_output1.x * sin( mulTime2 ) * _Size )
+( _WobbleZ * cache_output1.z * sin( mulTime2 ) * _Size )
+ cache_output1.y);
float alpha = clamp((cache_output_3 - cache_output2 )/ _Falloff, 0 , 1 ) ;
float cache_output3 = step(cache_output_3 - 0.1, cache_output2);
float4 cache_output4 = lerp(_Color + rim_color, _Color3, cache_output3);
float4 finalcolor = lerp(_Color2 , cache_output4, facing);
return fixed4(finalcolor.rgb + rim_color.rgb , alpha);
}
ENDCG
}
}
}
cs部分
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MyWobble : MonoBehaviour
{
Renderer rend;
Vector3 lastPos;
Vector3 velocity;
public float MaxWobble = 0.03f;
public float WobbleSpeed = 1f;
public float Recovery = 1f;
float wobbleAmountX;
float wobbleAmountZ;
float wobbleAmountToAddX;
float wobbleAmountToAddZ;
float pulse;
float time = 0.5f;
// Start is called before the first frame update
void Start()
{
rend = GetComponent<Renderer>();
}
// Update is called once per frame
void Update()
{
time += Time.deltaTime;
// decrease wobble over time
wobbleAmountToAddX = Mathf.Lerp(wobbleAmountToAddX, 0, Time.deltaTime * (Recovery));
wobbleAmountToAddZ = Mathf.Lerp(wobbleAmountToAddZ, 0, Time.deltaTime * (Recovery));
// make a sine wave of the decreasing wobble
//pulse = 2 * Mathf.PI * WobbleSpeed;
//wobbleAmountX = wobbleAmountToAddX * Mathf.Sin(pulse * time);
//wobbleAmountZ = wobbleAmountToAddZ * Mathf.Sin(pulse * time);
// send it to the shader
rend.material.SetFloat("_WobbleX", wobbleAmountToAddX);
rend.material.SetFloat("_WobbleZ", wobbleAmountToAddZ);
//print(wobbleAmountX + "--------------" +wobbleAmountZ);
// velocity
velocity = (lastPos - transform.position) / Time.deltaTime;
print(velocity);
// add clamped velocity to wobble
wobbleAmountToAddX += Mathf.Clamp((velocity.x) * MaxWobble, -MaxWobble, MaxWobble);
wobbleAmountToAddZ += Mathf.Clamp((velocity.z ) * MaxWobble, -MaxWobble, MaxWobble);
// keep last position
lastPos = transform.position;
}
}
总结
以上就是水瓶制作的流程,欢迎讨论。特别是在上面的链接里面直接用object->world的矩阵乘以对象空间顶点的xyz,这里我是不太理解的,object->world矩阵不是一个4x4的矩阵,怎么能乘以一个3维坐标呢。而且算出来的坐标也不受世界空间影响,而且刚好能用,神奇。
最后
以上就是矮小蜗牛为你收集整理的水瓶效果制作前言一、实现效果效果分析总结的全部内容,希望文章能够帮你解决水瓶效果制作前言一、实现效果效果分析总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复