class: center, middle # Creación de Videojuegos ### Lighting, Shaders, Animation --- class: center, middle # Let there be light --- # The Phong Reflection Model * The Phong Reflection Model is an empirical model for how lights are reflected off of surfaces * Consists of ambient, diffuse and specular components
--- # The Phong Reflection Model
R
V
N
L
$$ I = k_a \cdot i_a + \sum_m \left(k_d (L_m \cdot N) i_d + k_s (R_m \cdot V)^\alpha i_s \right) $$ --- class: small # How do we use this information? * Flat shading: Calculate one value for the center of each triangle, and color the entire triangle that way * Gouraud shading: Calculate one value for each vertex of a triangle and interpolate the color in between * Phong shading: Interpolate normals between the vertices of a triangle, and calculate the color for each pixel
--- # Note * Phong reflection *model*: Equation to determine how light affects the color of a point * Phong shading/Gouraud shading/Flat shading: Methods to *use* this equation * Shader: The *implementation*/*program* that performs shading --- # Alternatives: Non-Photorealistic Shading * Toon/Cel shaders!
--- # Toon shader
(Legend of Zelda: Breath of the Wild) --- class: center, middle # Shaders --- # Shaders * Vertex shaders: Operate on each vertex * Fragment/Pixel shaders: Operate on each displayable unit * Other shaders we are not going to talk about here: Geometry shader, tesselation shader, computer shader * Need to be fast! * Are written in a shader language --- # Shader languages * ARB assembly (legacy) * Cg (deprecated) * GLSL (OpenGL) * HLSL (DirectX, used by Unity) * Tool: HLSL2GLSL --- # HLSL Shaders ```HLSL Shader "ShaderName" { Properties { // Properties you can set in Unity } SubShader { // Shader code } } ``` --- # HLSL Shader properties ```HLSL Properties { _MyTexture ("My texture", 2D) = "white" {} _MyInt ("My integer", Int) = 2 _MyFloat ("My float", Float) = 1.5 _MyRange ("My range", Range(0.0, 1.0)) = 0.5 _MyColor ("My color", Color) = (1, 0, 0, 1) // (R, G, B, A) _MyVector ("My Vector4", Vector) = (0, 0, 0, 0) // (x, y, z, w) } ``` --- # HLSL Shader properties as ```HLSL SubShader { sampler2D _MyTexture; // 2D = sampler2D int _MyInt; float _MyFloat; float _MyRange; half4 _MyColor; // Color = half4 float4 _MyVector; // Vector = float4 // Code } ``` --- class: small # Vertex and Pixel Shader ```HLSL Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag struct vertInput { float4 pos : POSITION; }; struct vertOutput { float4 pos : SV_POSITION; }; vertOutput vert(vertInput input) { vertOutput o; o.pos = mul(UNITY_MATRIX_MVP, input.pos); return o; } half4 frag(vertOutput output) : COLOR { return half4(1.0, 0.0, 0.0, 1.0); } ENDCG } ``` --- class: small # What can you pass in and out of these shaders? * Possible inputs: - `POSITION` - `NORMAL` - `COLOR` - `TEXCOORD0`, `TEXCOORD1`, ... - etc. * Possible outputs: - `SV_POSITION` - `COLOR` - `TEXCOORD0`, `TEXCOORD1`, ... - etc. --- class: small # What can you do? * Vertex Shader: - Change positions of vertices - Modify texture coordinates - Store information for the pixel shader * Pixel Shader: - Change how a pixel is drawn - Add noise or distortion effects (water, glass) --- class: small # How do you use a Shader in Unity? * Create a new Asset: - Surface Shader - Unlit Shader for a Vertex *and* Pixel Shader * On your material, select your Shader from the dropdown box
--- class: small # Wobbling In the Vertex Shader: ```HLSL fixed4 col = tex2Dlod(_NoiseTex, float4(v.uv, 0, 0)); float d = 1 - (v.uv[1]*v.uv[1] + v.uv[0]*v.uv[0]) + col[0]; o.vertex = UnityObjectToClipPos(v.vertex + float4(0.0, 0.5*(sin(_Time[2] + d*8) / 2 + 0.5), 0.0, 0.0)); o.tex = float4(d,0,0,0); ``` In the Pixel Shader: ```HLSL fixed4 col = float4(0, 0, (sin(_Time[2] + i.tex[0]*8)/2 + 0.5),1); ``` --- # Wobbling: Result
Shader source code
--- class: center, middle # Interpolation --- # Where do we use Interpolation * Whenever we have two (or more) values where we want intermediate values, we use interpolation * Values can be numbers, positions, rotations, colors, etc. * The simplest case: Linear interpolation, where the value half-way between two values is their average * However, we can generalize! --- # The problem We have two values: a and b, and we want a function $$ f: [0,1] \mapsto [a,b] $$ such that $$ f(0) = a $$ $$ f(1) = b $$ (And values between 0 and 1 are between a and b) --- # Linear interpolation One such function, which we have called Linear Interpolation (Lerp) is: $$ f(t) = (1-t)\cdot a + t \cdot b $$ But this results in constant speed over time, meaning we start and stop abruptly. -- Unless ... -- What if we start and stop slowly? --- # Composing functions If we had a function $$ g : [0,1] \mapsto [0,1] $$ with $$ g(0) = 0, g(1) = 1 $$ we can compose it with f to get another interpolation function: $$ f(g(t)) $$ --- class: small # Useful functions How about a cosine?
Sine_one_period.svg - a nice plot of the sine function
Sine(sin)-function from Wikimedia Commons plot-range: 0 to 2pi plotted with three different cubic bezier-curves the bezier-controll-points are calculated to give a very accurate result. symbols in "Computer Modern" (TeX) font embedded created with a plain text editor using GNU/Linux about: http://commons.wikimedia.org/wiki/Image:Sine_one_period.svg source: http://commons.wikimedia.org/ rights: GNU Free Documentation license, Creative Commons Attribution ShareAlike license
But that's not mapping from [0,1] to [0,1]?! -- We need to do two things: * Map the input of the cosine from [0,1] to something else * Map the output of the cosine to [0,1] --- class: small # Useful functions Which area do we want?
Sine_one_period.svg - a nice plot of the sine function
Sine(sin)-function from Wikimedia Commons plot-range: 0 to 2pi plotted with three different cubic bezier-curves the bezier-controll-points are calculated to give a very accurate result. symbols in "Computer Modern" (TeX) font embedded created with a plain text editor using GNU/Linux about: http://commons.wikimedia.org/wiki/Image:Sine_one_period.svg source: http://commons.wikimedia.org/ rights: GNU Free Documentation license, Creative Commons Attribution ShareAlike license
-- Map to the domain: $$ c(t) = \cos(\pi + t*\pi) $$ --- class: small # Useful functions Which area do we want?
Sine_one_period.svg - a nice plot of the sine function
Sine(sin)-function from Wikimedia Commons plot-range: 0 to 2pi plotted with three different cubic bezier-curves the bezier-controll-points are calculated to give a very accurate result. symbols in "Computer Modern" (TeX) font embedded created with a plain text editor using GNU/Linux about: http://commons.wikimedia.org/wiki/Image:Sine_one_period.svg source: http://commons.wikimedia.org/ rights: GNU Free Documentation license, Creative Commons Attribution ShareAlike license
But we also need to fix the result: $$ g(t) = \cos(\pi + t*\pi)/2 + 0.5 $$ --- # Useful functions Here's another useful one: A sigmoid function
Graph of Logistics Curve
Originally Produced by GNUPLOT 4.2 patchlevel 2, hand compressed
0
0.5
1
−6
−4
−2
0
2
4
6
Corrected: $$ g(t) = \frac{1}{2\cdot(1+e^{-(12\cdot t - 6)})} + 0.5 $$ --- # Comparison
Unity gives you: `Mathf.SmoothStep` --- class: center, middle # Animation --- # T-Pose
--- # 3D Character Models * The T-Pose exposes many parts of the model to be able to fill holes * Characters are therefore usually modeled this way * But we want the character to move! * How do people move? * Skeletons and muscles! --- # Skeletons
--- # Attaching the Skin
--- # Muscles * The animation clips are the "muscles" * They determine where each bone should go and when * The animator often only defines *keyframes* beTWEEN which the system *interpolates* * To get smooth motion, techniques like we discussed earlier are often used * [Mixamo](https://www.mixamo.com/#/?page=1&query=gangnam&type=Motion%2CMotionPack) --- # Inverse Kinematics * To animate a character, we set the locations of its bones, which determined the position of its mesh parts (Forward Kinematic) * What if we want the inverse? For example, that the hand catches a ball? * This problem is called Inverse Kinematics * Forward Kinematic is easy * Inverse Kinematic is much harder --- # Inverse Kinematics: Challenges * There may be many ways for the hand to reach a certain location * There may be *no* ways, too * We also can't bend bone connections arbitrarily * (Most) humans can't bend their elbows backwards * You can solve this with some matrix math --- # Inverse Kinematics in Unity * Unity has built-in support for Inverse Kinematics (IK) for Humanoid characters * `SetIKPosition` and `SetIKPositionWeight` make the character reach for a point * You have to enable IK Pass in your Animation Controller Layer settings! ``` animator.SetIKPositionWeight(AvatarIKGoal.RightHand, 1); animator.SetIKPosition(AvatarIKGoal.RightHand, objToPickUp.position); ``` --- class: small # References * [Unity Shader tutorial](https://unity3d.com/learn/tutorials/topics/graphics/gentle-introduction-shaders) * [Phong Shading in Shaders](https://learnopengl.com/Lighting/Basic-Lighting) * [Vertex and Fragment shaders in Unity](https://www.alanzucconi.com/2015/07/01/vertex-and-fragment-shaders-in-unity3d/) * [Unity Documentation on SmoothStep](https://docs.unity3d.com/ScriptReference/Mathf.SmoothStep.html) * [Unity Documentation on Character Preparation](https://docs.unity3d.com/2017.3/Documentation/Manual/Preparingacharacterfromscratch.html) * [Unity Documentation on Inverse Kinematics](https://docs.unity3d.com/Manual/InverseKinematics.html)