358 lines
11 KiB
HLSL
358 lines
11 KiB
HLSL
// Copyright (C) 2021-2022, Dmitry Maluev (dmaluev@gmail.com). All rights reserved.
|
|
|
|
#include "Lib.hlsl"
|
|
#include "LibColor.hlsl"
|
|
#include "LibDeferredShading.hlsl"
|
|
#include "LibSurface.hlsl"
|
|
#include "LibTessellation.hlsl"
|
|
#include "DS_Terrain.inc.hlsl"
|
|
|
|
CBUFFER(0, UB_TerrainVS, g_ubTerrainVS)
|
|
CBUFFER(1, UB_TerrainFS, g_ubTerrainFS)
|
|
|
|
Texture2D g_texHeightVS : REG(t1, space0, t0);
|
|
SamplerState g_samHeightVS : REG(s1, space0, s0);
|
|
Texture2D g_texNormalVS : REG(t2, space0, t1);
|
|
SamplerState g_samNormalVS : REG(s2, space0, s1);
|
|
|
|
Texture2D g_texNormal : REG(t1, space1, t2);
|
|
SamplerState g_samNormal : REG(s1, space1, s2);
|
|
Texture2D g_texBlend : REG(t2, space1, t3);
|
|
SamplerState g_samBlend : REG(s2, space1, s3);
|
|
Texture2DArray g_texLayers : REG(t3, space1, t4);
|
|
SamplerState g_samLayers : REG(s3, space1, s4);
|
|
Texture2DArray g_texLayersN : REG(t4, space1, t5);
|
|
SamplerState g_samLayersN : REG(s4, space1, s5);
|
|
Texture2DArray g_texLayersX : REG(t5, space1, t6);
|
|
SamplerState g_samLayersX : REG(s5, space1, s6);
|
|
Texture2D g_texDetail : REG(t6, space1, t7);
|
|
SamplerState g_samDetail : REG(s6, space1, s7);
|
|
Texture2D g_texDetailN : REG(t7, space1, t8);
|
|
SamplerState g_samDetailN : REG(s7, space1, s8);
|
|
Texture2D g_texStrass : REG(t8, space1, t9);
|
|
SamplerState g_samStrass : REG(s8, space1, s9);
|
|
|
|
struct VSI
|
|
{
|
|
VK_LOCATION_POSITION int4 pos : POSITION;
|
|
VK_LOCATION(16) int4 posPatch : INSTDATA0;
|
|
VK_LOCATION(17) int4 layerForChannel : INSTDATA1;
|
|
};
|
|
|
|
struct VSO
|
|
{
|
|
float4 pos : SV_Position;
|
|
float3 nrmWV : TEXCOORD0;
|
|
#if !defined(DEF_DEPTH)
|
|
float4 layerForChannel : TEXCOORD1;
|
|
#if !defined(DEF_DEPTH) && !defined(DEF_SOLID_COLOR)
|
|
float3 tcBlend : TEXCOORD2;
|
|
float4 tcLayer_tcMap : TEXCOORD3;
|
|
#endif
|
|
#endif
|
|
};
|
|
|
|
static const float g_detailScale = 8.0;
|
|
static const float g_strassScale = 16.0;
|
|
static const float g_layerScale = 1.0 / 8.0;
|
|
|
|
#ifdef _VS
|
|
VSO mainVS(VSI si)
|
|
{
|
|
VSO so;
|
|
|
|
const float3 eyePos = g_ubTerrainVS._eyePos_invMapSide.xyz;
|
|
const float invMapSide = g_ubTerrainVS._eyePos_invMapSide.w;
|
|
|
|
const float2 edgeCorrection = si.pos.yw;
|
|
si.pos.yw = 0.0;
|
|
float3 pos = si.pos.xyz + si.posPatch.xyz;
|
|
const float2 tcMap = pos.xz * invMapSide + 0.5; // Range [0, 1).
|
|
const float2 posBlend = pos.xz + edgeCorrection * 0.5;
|
|
|
|
// <HeightAndNormal>
|
|
float3 inNrm;
|
|
{
|
|
pos.y = UnpackTerrainHeight(g_texHeightVS.SampleLevel(g_samHeightVS, tcMap + (8.0 * invMapSide), 4.0).r);
|
|
const float distToEye = distance(pos, eyePos);
|
|
const float geomipsLod = log2(clamp(distToEye * (2.0 / 100.0), 1.0, 18.0));
|
|
const float geomipsLodFrac = frac(geomipsLod);
|
|
const float geomipsLodBase = floor(geomipsLod);
|
|
const float geomipsLodNext = geomipsLodBase + 1.0;
|
|
const float2 texelCenterAB = (0.5 * invMapSide) * exp2(float2(geomipsLodBase, geomipsLodNext));
|
|
const float yA = UnpackTerrainHeight(g_texHeightVS.SampleLevel(g_samHeightVS, tcMap + texelCenterAB.xx, geomipsLodBase).r);
|
|
const float yB = UnpackTerrainHeight(g_texHeightVS.SampleLevel(g_samHeightVS, tcMap + texelCenterAB.yy, geomipsLodNext).r);
|
|
pos.y = lerp(yA, yB, geomipsLodFrac);
|
|
|
|
const float4 normalSam = g_texNormalVS.SampleLevel(g_samNormalVS, tcMap + texelCenterAB.xx, geomipsLodBase);
|
|
inNrm = float3(normalSam.x, 0, normalSam.y) * 2.0 - 1.0;
|
|
inNrm.y = ComputeNormalZ(inNrm.xz);
|
|
}
|
|
// </HeightAndNormal>
|
|
|
|
so.pos = MulTessPos(float4(pos, 1), g_ubTerrainVS._matV, g_ubTerrainVS._matVP);
|
|
so.nrmWV = mul(inNrm, (float3x3)g_ubTerrainVS._matWV);
|
|
#if !defined(DEF_DEPTH)
|
|
so.layerForChannel = si.layerForChannel;
|
|
#if !defined(DEF_DEPTH) && !defined(DEF_SOLID_COLOR)
|
|
so.tcBlend.xy = posBlend * invMapSide + 0.5;
|
|
so.tcBlend.z = pos.y;
|
|
so.tcLayer_tcMap.xy = pos.xz * g_layerScale;
|
|
so.tcLayer_tcMap.zw = (pos.xz + 0.5) * invMapSide + 0.5; // Texel's center.
|
|
#endif
|
|
#endif
|
|
|
|
return so;
|
|
}
|
|
#endif
|
|
|
|
_HSO_STRUCT;
|
|
|
|
#ifdef _HS
|
|
PCFO PatchConstFunc(const OutputPatch<HSO, 3> outputPatch)
|
|
{
|
|
PCFO so;
|
|
_HS_PCF_BODY(g_ubTerrainVS._matP);
|
|
return so;
|
|
}
|
|
|
|
[domain("tri")]
|
|
[maxtessfactor(7.0)]
|
|
[outputcontrolpoints(3)]
|
|
[outputtopology("triangle_cw")]
|
|
[partitioning(_PARTITION_METHOD)]
|
|
[patchconstantfunc("PatchConstFunc")]
|
|
HSO mainHS(InputPatch<VSO, 3> inputPatch, uint id : SV_OutputControlPointID)
|
|
{
|
|
HSO so;
|
|
|
|
_HS_PN_BODY(nrmWV, g_ubTerrainVS._matP, g_ubTerrainVS._viewportSize);
|
|
|
|
_HS_COPY(pos);
|
|
_HS_COPY(nrmWV);
|
|
#if !defined(DEF_DEPTH)
|
|
_HS_COPY(layerForChannel);
|
|
#if !defined(DEF_DEPTH) && !defined(DEF_SOLID_COLOR)
|
|
_HS_COPY(tcBlend);
|
|
_HS_COPY(tcLayer_tcMap);
|
|
#endif
|
|
#endif
|
|
|
|
return so;
|
|
}
|
|
#endif
|
|
|
|
#ifdef _DS
|
|
[domain("tri")]
|
|
VSO mainDS(_IN_DS)
|
|
{
|
|
VSO so;
|
|
|
|
_DS_INIT_FLAT_POS;
|
|
_DS_INIT_SMOOTH_POS;
|
|
|
|
so.pos = ApplyProjection(smoothPosWV, g_ubTerrainVS._matP);
|
|
_DS_COPY(nrmWV);
|
|
#if !defined(DEF_DEPTH)
|
|
_DS_COPY(layerForChannel);
|
|
#if !defined(DEF_DEPTH) && !defined(DEF_SOLID_COLOR)
|
|
_DS_COPY(tcBlend);
|
|
_DS_COPY(tcLayer_tcMap);
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef DEF_DEPTH
|
|
// Fade to non-tess mesh at 80 meters. LOD 1 starts at 100 meters.
|
|
const float tessStrength = saturate((1.0 - saturate(smoothPosWV.z / -80.0)) * 4.0);
|
|
const float3 posWV = lerp(flatPosWV, smoothPosWV, tessStrength);
|
|
so.pos = ApplyProjection(posWV, g_ubTerrainVS._matP);
|
|
#endif
|
|
|
|
return so;
|
|
}
|
|
#endif
|
|
|
|
#ifdef _FS
|
|
DS_FSO mainFS(VSO si)
|
|
{
|
|
DS_FSO so;
|
|
DS_Reset(so);
|
|
|
|
#ifdef DEF_SOLID_COLOR
|
|
DS_SolidColor(so, si.layerForChannel.rgb);
|
|
#else
|
|
// Fix interpolation errors by rounding:
|
|
si.layerForChannel = round(si.layerForChannel);
|
|
const float2 tcLayer = si.tcLayer_tcMap.xy;
|
|
const float2 tcMap = si.tcLayer_tcMap.zw;
|
|
|
|
const float4 blendSam = g_texBlend.Sample(g_samBlend, si.tcBlend.xy);
|
|
const float4 weights = float4(blendSam.rgb, 1.0 - dot(blendSam.rgb, 1.0));
|
|
|
|
const float4 texEnabled = ceil(weights);
|
|
const float3 tcLayerR = float3(tcLayer * texEnabled.r, si.layerForChannel.r);
|
|
const float3 tcLayerG = float3(tcLayer * texEnabled.g, si.layerForChannel.g);
|
|
const float3 tcLayerB = float3(tcLayer * texEnabled.b, si.layerForChannel.b);
|
|
const float3 tcLayerA = float3(tcLayer * texEnabled.a, si.layerForChannel.a);
|
|
|
|
// <Basis>
|
|
float3 basisTan, basisBin, basisNrm;
|
|
{
|
|
const float4 basisSam = g_texNormal.Sample(g_samNormal, tcMap);
|
|
const float4 basis = basisSam * 2.0 - 1.0;
|
|
basisNrm = float3(basis.x, 0, basis.y);
|
|
basisTan = float3(0, basis.z, basis.w);
|
|
basisNrm.y = ComputeNormalZ(basisNrm.xz);
|
|
basisTan.x = ComputeNormalZ(basisTan.yz);
|
|
basisBin = cross(basisTan, basisNrm);
|
|
}
|
|
_TBN_SPACE(
|
|
mul(basisTan, (float3x3)g_ubTerrainFS._matWV),
|
|
mul(basisBin, (float3x3)g_ubTerrainFS._matWV),
|
|
mul(basisNrm, (float3x3)g_ubTerrainFS._matWV));
|
|
// </Basis>
|
|
|
|
// <Albedo>
|
|
float3 albedo;
|
|
float detailStrength;
|
|
float roughStrength;
|
|
{
|
|
float3 accAlbedo = 0.0;
|
|
accAlbedo += g_texLayers.Sample(g_samLayers, tcLayerR).rgb * weights.r;
|
|
accAlbedo += g_texLayers.Sample(g_samLayers, tcLayerG).rgb * weights.g;
|
|
accAlbedo += g_texLayers.Sample(g_samLayers, tcLayerB).rgb * weights.b;
|
|
accAlbedo += g_texLayers.Sample(g_samLayers, tcLayerA).rgb * weights.a;
|
|
albedo = accAlbedo;
|
|
|
|
static float vDetailStrength[_MAX_TERRAIN_LAYERS] = (float[_MAX_TERRAIN_LAYERS])g_ubTerrainFS._vDetailStrength;
|
|
static float vRoughStrength[_MAX_TERRAIN_LAYERS] = (float[_MAX_TERRAIN_LAYERS])g_ubTerrainFS._vRoughStrength;
|
|
const float4 detailStrengthForChannel = float4(
|
|
vDetailStrength[si.layerForChannel.r],
|
|
vDetailStrength[si.layerForChannel.g],
|
|
vDetailStrength[si.layerForChannel.b],
|
|
vDetailStrength[si.layerForChannel.a]);
|
|
const float4 roughStrengthForChannel = float4(
|
|
vRoughStrength[si.layerForChannel.r],
|
|
vRoughStrength[si.layerForChannel.g],
|
|
vRoughStrength[si.layerForChannel.b],
|
|
vRoughStrength[si.layerForChannel.a]);
|
|
detailStrength = dot(detailStrengthForChannel, weights);
|
|
roughStrength = dot(roughStrengthForChannel, weights);
|
|
}
|
|
// </Albedo>
|
|
|
|
// <Detail>
|
|
float3 detailSam;
|
|
float3 detailNSam;
|
|
{
|
|
detailSam = g_texDetail.Sample(g_samDetail, tcLayer * g_detailScale).rgb;
|
|
detailNSam = g_texDetailN.Sample(g_samDetailN, tcLayer * g_detailScale).rgb;
|
|
}
|
|
albedo = saturate(albedo * lerp(0.5, detailSam, detailStrength) * 2.0);
|
|
// </Detail>
|
|
|
|
// <Water>
|
|
float waterRoughnessScale;
|
|
float waterRoughnessMin;
|
|
{
|
|
const float dryMask = saturate(si.tcBlend.z);
|
|
const float dryMask3 = dryMask * dryMask * dryMask;
|
|
const float wetMask = 1.0 - dryMask;
|
|
const float wetMask3 = wetMask * wetMask * wetMask;
|
|
albedo *= 0.4 + 0.6 * dryMask3;
|
|
waterRoughnessScale = 1.0 - 0.8 * saturate(dryMask * wetMask3 * 16.0);
|
|
waterRoughnessMin = wetMask3;
|
|
}
|
|
// </Water>
|
|
|
|
// <Normal>
|
|
float3 normalWV;
|
|
float3 tangentWV;
|
|
{
|
|
float4 accNormal = 0.0;
|
|
accNormal += g_texLayersN.Sample(g_samLayersN, tcLayerR) * weights.r;
|
|
accNormal += g_texLayersN.Sample(g_samLayersN, tcLayerG) * weights.g;
|
|
accNormal += g_texLayersN.Sample(g_samLayersN, tcLayerB) * weights.b;
|
|
accNormal += g_texLayersN.Sample(g_samLayersN, tcLayerA) * weights.a;
|
|
|
|
accNormal.rg = saturate(accNormal.rg * lerp(0.5, detailNSam.rg, detailStrength) * 2.0);
|
|
|
|
const float3 normalTBN = NormalMapFromBC5(accNormal, -3.0);
|
|
normalWV = normalize(mul(normalTBN, matFromTBN));
|
|
tangentWV = normalize(mul(cross(normalTBN, cross(float3(0, 1, 0), normalTBN)), matFromTBN));
|
|
}
|
|
// </Normal>
|
|
|
|
// <X>
|
|
float occlusion;
|
|
float roughness;
|
|
float metallic;
|
|
float emission;
|
|
float wrapDiffuse;
|
|
float anisoSpec;
|
|
{
|
|
float4 accX = 0.0;
|
|
accX += g_texLayersX.Sample(g_samLayersX, tcLayerR) * weights.r;
|
|
accX += g_texLayersX.Sample(g_samLayersX, tcLayerG) * weights.g;
|
|
accX += g_texLayersX.Sample(g_samLayersX, tcLayerB) * weights.b;
|
|
accX += g_texLayersX.Sample(g_samLayersX, tcLayerA) * weights.a;
|
|
|
|
occlusion = accX.r;
|
|
roughness = accX.g;
|
|
const float2 metallic_wrapDiffuse = CleanMutexChannels(SeparateMutexChannels(accX.b));
|
|
const float2 emission_anisoSpec = CleanMutexChannels(SeparateMutexChannels(accX.a));
|
|
metallic = metallic_wrapDiffuse.r;
|
|
emission = emission_anisoSpec.r * 10000.0;
|
|
wrapDiffuse = metallic_wrapDiffuse.g;
|
|
anisoSpec = emission_anisoSpec.g;
|
|
}
|
|
// </X>
|
|
|
|
// <Rim>
|
|
{
|
|
const float rim = saturate(1.0 - normalWV.z);
|
|
const float rimSq = rim * rim;
|
|
roughness = saturate(roughness + rimSq * 3.0 * wrapDiffuse);
|
|
}
|
|
// </Rim>
|
|
|
|
// <Strass>
|
|
{
|
|
const float strass = g_texStrass.Sample(g_samStrass, tcLayer * g_strassScale).r;
|
|
roughness *= 1.0 - clamp(strass * roughStrength * 4.0, 0.0, 0.99);
|
|
}
|
|
// </Strass>
|
|
|
|
roughness = max(roughness * waterRoughnessScale, waterRoughnessMin);
|
|
|
|
{
|
|
DS_SetAlbedo(so, albedo);
|
|
DS_SetSSSHue(so, 0.5);
|
|
|
|
DS_SetNormal(so, normalWV);
|
|
DS_SetEmission(so, emission);
|
|
DS_SetMotionBlur(so, 1.0);
|
|
|
|
DS_SetOcclusion(so, occlusion);
|
|
DS_SetRoughness(so, roughness);
|
|
DS_SetMetallic(so, metallic);
|
|
DS_SetWrapDiffuse(so, wrapDiffuse);
|
|
|
|
DS_SetTangent(so, tangentWV);
|
|
DS_SetAnisoSpec(so, anisoSpec);
|
|
DS_SetRoughDiffuse(so, roughStrength);
|
|
}
|
|
#endif
|
|
|
|
return so;
|
|
}
|
|
#endif
|
|
|
|
//@main:#
|
|
//@main:#Tess TESS (VHDF)
|
|
|
|
//@main:#Depth DEPTH (V)
|
|
//@main:#DepthTess DEPTH TESS (VHD)
|
|
|
|
//@main:#SolidColor SOLID_COLOR
|