воскресенье, 1 февраля 2015 г.

HLSL: Шейдер воды

Какое то время назад я показывал, в своей публикации, видео с примером реализации воды.


По многочисленным просьбам решил открыть код своего шейдера Water.fx:

float4x4 mWorld;
float4x4 mWorldBack;
float4x4 mWVP;
float4x4 mWVPreflection;

float3 LightDir;
float3 ViewDir;

float waveHeight;
float2 windDelCoord;

float width = 1000;
float heught = 1000;

Texture texRefraction;
sampler RefractionMapSampler = sampler_state
{
texture =
magfilter = ANISOTROPIC;
minfilter = LINEAR;
mipfilter = LINEAR;
MaxAnisotropy = 8;
AddressU = mirror;
AddressV = mirror;
};

Texture texReflection;
sampler ReflectionMapSampler = sampler_state
{
texture =
magfilter = ANISOTROPIC;
minfilter = LINEAR;
mipfilter = LINEAR;
MaxAnisotropy = 8;
AddressU = mirror;
AddressV = mirror;
};

Texture texBumpMapA;
sampler BumpMapSamplerA = sampler_state
{
texture =
magfilter = ANISOTROPIC;
minfilter = LINEAR;
mipfilter = LINEAR;
MaxAnisotropy = 8;
AddressU = mirror;
AddressV = mirror;
};

Texture texBumpMapB;
sampler BumpMapSamplerB = sampler_state
{
texture =
magfilter = LINEAR;
minfilter = LINEAR;
mipfilter = LINEAR;
AddressU = mirror;
AddressV = mirror;
};

struct VertexShaderInput
{
    float4 Position : POSITION0;
    float2 TexCoord : TEXCOORD0;
};

struct VertexShaderOutput
{
float4 Position : POSITION0;
float4 TexCoordReflectionMap : TEXCOORD1;
float4 TexCoordRefractionMap : TEXCOORD2;
float2 TexCoordBumpMap : TEXCOORD3;
float4 Position3D : TEXCOORD4;
    float3 LightTS : TEXCOORD5;
float3 ViewTS : TEXCOORD6;
};

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
VertexShaderOutput output;

float4 worldPosition = mul(input.Position, mWorld);
output.Position = mul(input.Position, mWVP);
 
output.TexCoordReflectionMap = mul(input.Position, mWVPreflection);
output.TexCoordRefractionMap = output.Position;
output.TexCoordBumpMap = input.TexCoord;
output.Position3D = worldPosition;
 
    float3 tangent = float3(1,0,0);
float3 binormal = float3(0,0,1);
float3 normal = float3(0,1,0);
float3x3 matTS = float3x3( tangent, binormal, normal );

float3 Light = mul(LightDir, mWorldBack) - worldPosition;
float3 View = mul(ViewDir, mWorldBack) - worldPosition;

output.LightTS = normalize(mul(Light, matTS));
output.ViewTS = normalize(mul(View, matTS));

return output;
}

float4 cSpecularColor = float4(1, 1, 1, 1);
float fSpecularExponent = 300;

float4 WaterPS(VertexShaderOutput input) : COLOR0
{
float3 Distance = input.Position3D - ViewDir;
float rDistance = abs(length(Distance));
float rDistanceY = abs(length(Distance.y));
float rDistanceXZ = abs(length(Distance.xz));
float lerpDistanceA = 1 - saturate(rDistanceXZ / 1000);
float lerpDistanceB = 0.0;
if(rDistance > 50.0f)
{
lerpDistanceB = 1 - saturate(rDistance) / (1 * width);
}
else
{
lerpDistanceB = saturate(rDistanceXZ / 50.0f);
}

// reflection
float2 TexCoorReflection;
TexCoorReflection.x = input.TexCoordReflectionMap.x / input.TexCoordReflectionMap.w / 2.0f + 0.5f;
TexCoorReflection.y = -input.TexCoordReflectionMap.y / input.TexCoordReflectionMap.w / 2.0f + 0.5f;
 
float3 bumpNormalA = tex2D(BumpMapSamplerA, input.TexCoordBumpMap * 16 + windDelCoord) * 2 - 1;
float3 bumpNormalB = tex2D(BumpMapSamplerB, input.TexCoordBumpMap * 256 - windDelCoord * 2 ) * 2 - 1;
 
float2 moveA = bumpNormalA.xy * waveHeight * lerpDistanceA;
float2 moveB = bumpNormalB.xy * 0.2 * lerpDistanceB;
float2 TextCoordMove = moveA + moveB;
 
float2 TexCoordsMoveReflection = TexCoorReflection * 0.75f + TextCoordMove;

float4 reflectiveColor = tex2D(ReflectionMapSampler, TexCoordsMoveReflection) * 0.7f;

float3 NormalTS = tex2D( BumpMapSamplerA, input.TexCoordBumpMap * 16 + windDelCoord) * 2 - 1;
NormalTS = normalize( NormalTS);

float3 vReflectionTS = normalize( 2 * dot( input.ViewTS, NormalTS * 2 ) * NormalTS - input.ViewTS );
float fRdotL = dot( vReflectionTS, input.LightTS );
float4 cSpecular = saturate( pow( fRdotL, fSpecularExponent )) * cSpecularColor;

float light = clamp(dot(NormalTS, input.LightTS) * (reflectiveColor + cSpecular), 0, 1);

reflectiveColor += light * 0.9;

// refraction
float2 TexCoordRefraction;
TexCoordRefraction.x = input.TexCoordRefractionMap.x / input.TexCoordRefractionMap.w / 2.0f + 0.5f;
TexCoordRefraction.y = -input.TexCoordRefractionMap.y / input.TexCoordRefractionMap.w / 2.0f + 0.5f;
float2 TexCoordsMoveRefraction = TexCoordRefraction + TextCoordMove;// * lerpDistanceRefracion;
float4 refractiveColor = tex2D(RefractionMapSampler, TexCoordsMoveRefraction);

float3 eyeVector = normalize(ViewDir - input.Position3D);
float3 normalVector = float3(0,1,0);
float fNdotV = dot(eyeVector, normalVector);
float fFresnel = 1.0 - fNdotV;

// result
float4 resultColor = lerp(refractiveColor, reflectiveColor, fFresnel);
//resultColor.rgb *= (1.0 + resultColor.a * 1.2);
return resultColor;
}

technique Water
{
    pass P0
    {
        VertexShader = compile vs_2_0 VertexShaderFunction();
        PixelShader = compile ps_3_0 WaterPS();
    }
}

Unity 3D: Управление персонажем мышкой, вид с верху

Решаем задачу:

- Вид с верху;
- Персонаж (спрайт) должен перемещаться к месту клика мышью;
- Персонаж должен поворачиваться по направлению движения.

Решение:

using UnityEngine;

public class Player : MonoBehaviour
{

    private Vector3 _positionMove;
    private float _speed = 1f;

    private void Start()
    {
        _positionMove = transform.position;
    }

    private void Update()
    {
        UpdateInput();
        UpdateMove();
    }

    private void UpdateInput()
    {
        if (Input.GetMouseButtonDown(0))
        {
            _positionMove =
                UnityEngine.Camera.main.ScreenToWorldPoint(Input.mousePosition);
            _positionMove.z = 0;
        }
    }

    private void UpdateMove()
    {
        float moveDelta = (_positionMove - transform.position).magnitude;
        if (moveDelta <= _speed * Time.deltaTime)
        {
            transform.position = _positionMove;
            return;
        }
        Vector3 moveDir = _positionMove - transform.position;
        //
        float angle = Mathf.Atan2(moveDir.y, moveDir.x) * Mathf.Rad2Deg - 90;
        transform.rotation = Quaternion.Euler(new Vector3(0, 0, angle));
        //
        moveDir.Normalize();
        transform.position += moveDir * _speed * Time.deltaTime;
    }
}

Unity 3D: FPS script

Скриптов для подсчета fps в Unity много. Способы подсчета основываются на различных исходных данных и принципах. На данный момент выбрал такой вариант:

using UnityEngine;

public class Fps : MonoBehaviour
{
    private const float UpdateInterval = 1.0f;
    private float _timeleft;
    private float _lastTime;
    private float _timeSpan;
    private int _lastFrame;
    private int _frames;
    private float _fps;
   
    void Update()
    {
        _timeleft += Time.deltaTime;
        if (_timeleft > UpdateInterval)
        {
            _timeleft -= UpdateInterval;
            _frames = Time.frameCount - _lastFrame;
            _lastFrame = Time.frameCount;
            _timeSpan = Time.realtimeSinceStartup - _lastTime;
            _lastTime = Time.realtimeSinceStartup;
            _fps = Mathf.RoundToInt(_frames/_timeSpan);
        }
    }

    void OnGUI()
    {
        GUI.Box(new Rect(10, 10, 70, 25), string.Format("fps {0}", _fps));
    }
}