Materials Shaders
Π‘ΠΈΡΡΠ΅ΠΌΠ° ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΎΠ²
ΠΠ°ΡΠ΅ΡΠΈΠ°Π»Ρ Π² Wudgine ΠΎΠΏΡΠ΅Π΄Π΅Π»ΡΡΡ Π²ΠΈΠ·ΡΠ°Π»ΡΠ½ΡΠ΅ ΡΠ²ΠΎΠΉΡΡΠ²Π° ΠΏΠΎΠ²Π΅ΡΡ Π½ΠΎΡΡΠ΅ΠΉ ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ². Π‘ΠΈΡΡΠ΅ΠΌΠ° ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΎΠ² ΡΠ΅ΡΠ½ΠΎ ΠΈΠ½ΡΠ΅Π³ΡΠΈΡΠΎΠ²Π°Π½Π° Ρ ΡΠΈΠ·ΠΈΡΠ΅ΡΠΊΠΈ ΠΊΠΎΡΡΠ΅ΠΊΡΠ½ΡΠΌ ΡΠ΅Π½Π΄Π΅ΡΠΈΠ½Π³ΠΎΠΌ (PBR).
ΠΡΠ½ΠΎΠ²Π½ΡΠ΅ ΡΠ²ΠΎΠΉΡΡΠ²Π° ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΎΠ²
// ΠΡΠΈΠΌΠ΅Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΠΈ Π½Π°ΡΡΡΠΎΠΉΠΊΠΈ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»Π°
Material material = Material::create("MyMaterial");
// ΠΠ°Π·ΠΎΠ²ΡΠ΅ ΡΠ΅ΠΊΡΡΡΡΡ PBR
material.setTexture(TextureType::Albedo, "assets/textures/metal/albedo.png");
material.setTexture(TextureType::Normal, "assets/textures/metal/normal.png");
material.setTexture(TextureType::Metallic, "assets/textures/metal/metallic.png");
material.setTexture(TextureType::Roughness, "assets/textures/metal/roughness.png");
material.setTexture(TextureType::AmbientOcclusion, "assets/textures/metal/ao.png");
material.setTexture(TextureType::Emissive, "assets/textures/metal/emissive.png");
// ΠΠ°ΡΠ°ΠΌΠ΅ΡΡΡ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»Π°
material.setParameter("albedoFactor", Vector3(1.0f, 1.0f, 1.0f));
material.setParameter("metallicFactor", 1.0f);
material.setParameter("roughnessFactor", 0.5f);
material.setParameter("emissiveIntensity", 2.0f);
Π Π΅Π΄Π°ΠΊΡΠΎΡ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΎΠ²
Π Π΅Π΄Π°ΠΊΡΠΎΡ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΎΠ² ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ Π½Π°ΡΡΡΠ°ΠΈΠ²Π°ΡΡ ΡΠ²ΠΎΠΉΡΡΠ²Π° ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΎΠ² Π²ΠΈΠ·ΡΠ°Π»ΡΠ½ΠΎ:
- ΠΡΠ΅Π΄ΠΏΡΠΎΡΠΌΠΎΡΡ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»Π° Π² ΡΠ΅Π°Π»ΡΠ½ΠΎΠΌ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ
- ΠΠ°ΡΡΡΠΎΠΉΠΊΠ° ΡΠ΅ΠΊΡΡΡΡ ΠΈ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΎΠ²
- Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΡΠ»ΠΎΠΆΠ½ΡΡ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΎΠ² Ρ ΠΏΠΎΠΌΠΎΡΡΡ Π½ΠΎΠ΄ΠΎΠ²ΠΎΠ³ΠΎ ΡΠ΅Π΄Π°ΠΊΡΠΎΡΠ°
- ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° Π³ΠΎΡΠΎΠ²ΡΡ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΎΠ²
Π‘ΠΈΡΡΠ΅ΠΌΠ° ΡΠ΅ΠΉΠ΄Π΅ΡΠΎΠ²
Wudgine ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ ΡΠΎΠ²ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ ΡΠΈΡΡΠ΅ΠΌΡ ΡΠ΅ΠΉΠ΄Π΅ΡΠΎΠ², ΠΎΡΠ½ΠΎΠ²Π°Π½Π½ΡΡ Π½Π° GLSL Ρ ΡΠ°ΡΡΠΈΡΠ΅Π½ΠΈΡΠΌΠΈ Π΄Π»Ρ Vulkan.
Π‘ΡΡΡΠΊΡΡΡΠ° ΡΠ΅ΠΉΠ΄Π΅ΡΠΎΠ²
// ΠΡΠΈΠΌΠ΅Ρ Π²Π΅ΡΡΠΈΠ½Π½ΠΎΠ³ΠΎ ΡΠ΅ΠΉΠ΄Π΅ΡΠ° (vertex.glsl)
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inNormal;
layout(location = 2) in vec2 inTexCoord;
layout(location = 3) in vec3 inTangent;
layout(location = 4) in vec3 inBitangent;
layout(set = 0, binding = 0) uniform CameraUBO {
mat4 view;
mat4 proj;
vec3 position;
} camera;
layout(set = 1, binding = 0) uniform ModelUBO {
mat4 model;
mat4 normalMatrix;
} model;
layout(location = 0) out vec3 outWorldPos;
layout(location = 1) out vec2 outTexCoord;
layout(location = 2) out mat3 outTBN;
void main() {
vec4 worldPos = model.model * vec4(inPosition, 1.0);
outWorldPos = worldPos.xyz;
outTexCoord = inTexCoord;
vec3 T = normalize(vec3(model.normalMatrix * vec4(inTangent, 0.0)));
vec3 B = normalize(vec3(model.normalMatrix * vec4(inBitangent, 0.0)));
vec3 N = normalize(vec3(model.normalMatrix * vec4(inNormal, 0.0)));
outTBN = mat3(T, B, N);
gl_Position = camera.proj * camera.view * worldPos;
}
// ΠΡΠΈΠΌΠ΅Ρ ΡΡΠ°Π³ΠΌΠ΅Π½ΡΠ½ΠΎΠ³ΠΎ ΡΠ΅ΠΉΠ΄Π΅ΡΠ° (fragment.glsl)
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout(location = 0) in vec3 inWorldPos;
layout(location = 1) in vec2 inTexCoord;
layout(location = 2) in mat3 inTBN;
layout(set = 0, binding = 0) uniform CameraUBO {
mat4 view;
mat4 proj;
vec3 position;
} camera;
layout(set = 2, binding = 0) uniform MaterialUBO {
vec3 albedoFactor;
float metallicFactor;
float roughnessFactor;
float emissiveIntensity;
} material;
layout(set = 2, binding = 1) uniform sampler2D albedoMap;
layout(set = 2, binding = 2) uniform sampler2D normalMap;
layout(set = 2, binding = 3) uniform sampler2D metallicMap;
layout(set = 2, binding = 4) uniform sampler2D roughnessMap;
layout(set = 2, binding = 5) uniform sampler2D aoMap;
layout(set = 2, binding = 6) uniform sampler2D emissiveMap;
layout(location = 0) out vec4 outPosition;
layout(location = 1) out vec4 outNormal;
layout(location = 2) out vec4 outAlbedo;
layout(location = 3) out vec4 outPBR;
layout(location = 4) out vec4 outEmissive;
void main() {
// ΠΠ·Π²Π»Π΅ΡΠ΅Π½ΠΈΠ΅ Π΄Π°Π½Π½ΡΡ
ΠΈΠ· ΡΠ΅ΠΊΡΡΡΡ
vec3 albedo = texture(albedoMap, inTexCoord).rgb * material.albedoFactor;
float metallic = texture(metallicMap, inTexCoord).r * material.metallicFactor;
float roughness = texture(roughnessMap, inTexCoord).r * material.roughnessFactor;
float ao = texture(aoMap, inTexCoord).r;
vec3 emissive = texture(emissiveMap, inTexCoord).rgb * material.emissiveIntensity;
// ΠΠΎΡΠΌΠ°Π»ΠΈ ΠΈΠ· ΠΊΠ°ΡΡΡ Π½ΠΎΡΠΌΠ°Π»Π΅ΠΉ
vec3 normal = texture(normalMap, inTexCoord).rgb * 2.0 - 1.0;
normal = normalize(inTBN * normal);
// ΠΡΠ²ΠΎΠ΄ Π² G-Π±ΡΡΠ΅Ρ
outPosition = vec4(inWorldPos, 1.0);
outNormal = vec4(normal, 1.0);
outAlbedo = vec4(albedo, 1.0);
outPBR = vec4(metallic, roughness, ao, 1.0);
outEmissive = vec4(emissive, 1.0);
}
ΠΠΎΠΌΠΏΠΈΠ»ΡΡΠΈΡ ΡΠ΅ΠΉΠ΄Π΅ΡΠΎΠ²
Wudgine Π°Π²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ ΠΊΠΎΠΌΠΏΠΈΠ»ΠΈΡΡΠ΅Ρ ΡΠ΅ΠΉΠ΄Π΅ΡΡ ΠΏΡΠΈ ΡΠ±ΠΎΡΠΊΠ΅ ΠΏΡΠΎΠ΅ΠΊΡΠ°:
# ΠΠΎΠΌΠΏΠΈΠ»ΡΡΠΈΡ ΡΠ΅ΠΉΠ΄Π΅ΡΠΎΠ² Ρ ΠΏΠΎΠΌΠΎΡΡΡ glslc
glslc vertex.glsl -o vertex.spv
glslc fragment.glsl -o fragment.spv
Π¨Π΅ΠΉΠ΄Π΅ΡΠ½ΡΠ΅ Π²Π°ΡΠΈΠ°ΡΠΈΠΈ
Π‘ΠΈΡΡΠ΅ΠΌΠ° ΡΠ΅ΠΉΠ΄Π΅ΡΠΎΠ² Wudgine ΠΏΠΎΠ΄Π΄Π΅ΡΠΆΠΈΠ²Π°Π΅Ρ Π²Π°ΡΠΈΠ°ΡΠΈΠΈ Π΄Π»Ρ ΠΎΠΏΡΠΈΠΌΠΈΠ·Π°ΡΠΈΠΈ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡΠ΅Π»ΡΠ½ΠΎΡΡΠΈ:
// ΠΡΠΈΠΌΠ΅Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΡΠ΅ΠΉΠ΄Π΅ΡΠ° Ρ Π²Π°ΡΠΈΠ°ΡΠΈΡΠΌΠΈ
ShaderProgram shader = ShaderProgram::create("assets/shaders/pbr");
// ΠΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π²Π°ΡΠΈΠ°ΡΠΈΠΉ
shader.addVariation("HAS_ALBEDO_MAP", { true, false });
shader.addVariation("HAS_NORMAL_MAP", { true, false });
shader.addVariation("HAS_METALLIC_MAP", { true, false });
shader.addVariation("HAS_ROUGHNESS_MAP", { true, false });
shader.addVariation("HAS_AO_MAP", { true, false });
shader.addVariation("HAS_EMISSIVE_MAP", { true, false });
shader.addVariation("MAX_LIGHTS", { 1, 4, 8, 16 });
// ΠΠΎΠΌΠΏΠΈΠ»ΡΡΠΈΡ Π²ΡΠ΅Ρ
Π²Π°ΡΠΈΠ°ΡΠΈΠΉ
shader.compileVariations();
ΠΠΎΠ΄ΠΎΠ²ΡΠΉ ΡΠ΅Π΄Π°ΠΊΡΠΎΡ ΡΠ΅ΠΉΠ΄Π΅ΡΠΎΠ²
Wudgine Π²ΠΊΠ»ΡΡΠ°Π΅Ρ Π²ΠΈΠ·ΡΠ°Π»ΡΠ½ΡΠΉ Π½ΠΎΠ΄ΠΎΠ²ΡΠΉ ΡΠ΅Π΄Π°ΠΊΡΠΎΡ ΡΠ΅ΠΉΠ΄Π΅ΡΠΎΠ² Π΄Π»Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΡΠ»ΠΎΠΆΠ½ΡΡ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΎΠ² Π±Π΅Π· Π½Π°ΠΏΠΈΡΠ°Π½ΠΈΡ ΠΊΠΎΠ΄Π°:
- ΠΠ½ΡΡΠΈΡΠΈΠ²Π½ΡΠΉ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ Π΄Π»Ρ ΡΠΎΠ·Π΄Π°Π½ΠΈΡ ΡΠ΅ΠΉΠ΄Π΅ΡΠΎΠ²
- ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° Π³ΠΎΡΠΎΠ²ΡΡ Π½ΠΎΠ΄ΠΎΠ² Π΄Π»Ρ ΡΠ°Π·Π»ΠΈΡΠ½ΡΡ ΡΡΡΠ΅ΠΊΡΠΎΠ²
- ΠΡΠ΅Π΄ΠΏΡΠΎΡΠΌΠΎΡΡ Π² ΡΠ΅Π°Π»ΡΠ½ΠΎΠΌ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ
- ΠΠ²ΡΠΎΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠ°Ρ Π³Π΅Π½Π΅ΡΠ°ΡΠΈΡ GLSL ΠΊΠΎΠ΄Π°
ΠΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠΈΠ΅ ΡΠ΅ΠΉΠ΄Π΅ΡΡ
Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»ΡΡΠΊΠΈΡ ΡΠ΅ΠΉΠ΄Π΅ΡΠΎΠ² Π΄Π»Ρ ΡΠΏΠ΅ΡΠΈΠ°Π»ΡΠ½ΡΡ ΡΡΡΠ΅ΠΊΡΠΎΠ²:
// Π€ΡΠ°Π³ΠΌΠ΅Π½Ρ ΡΠ΅ΠΉΠ΄Π΅ΡΠ° Π΄Π»Ρ Π²ΠΎΠ΄Ρ
vec2 distortedUV = inTexCoord;
distortedUV.x += sin(inTexCoord.y * 40.0 + time * 0.5) * 0.01;
distortedUV.y += cos(inTexCoord.x * 40.0 + time * 0.5) * 0.01;
vec3 normal = texture(normalMap, distortedUV).rgb * 2.0 - 1.0;
normal = normalize(inTBN * normal);
// ΠΡΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ ΠΈ ΠΏΡΠ΅Π»ΠΎΠΌΠ»Π΅Π½ΠΈΠ΅
vec3 viewDir = normalize(camera.position - inWorldPos);
vec3 reflection = reflect(-viewDir, normal);
vec4 reflectionColor = texture(reflectionMap, reflection.xy);
// Π€ΡΠ°Π³ΠΌΠ΅Π½Ρ ΡΠ΅ΠΉΠ΄Π΅ΡΠ° Π΄Π»Ρ ΡΠΌΠ΅ΡΠΈΠ²Π°Π½ΠΈΡ ΡΠ΅ΠΊΡΡΡΡ ΠΏΠΎ Π²ΡΡΠΎΡΠ΅
float height = inWorldPos.y;
float sandThreshold = 0.1;
float grassThreshold = 5.0;
float rockThreshold = 15.0;
float snowThreshold = 25.0;
vec3 sandColor = texture(sandTexture, inTexCoord * 20.0).rgb;
vec3 grassColor = texture(grassTexture, inTexCoord * 15.0).rgb;
vec3 rockColor = texture(rockTexture, inTexCoord * 10.0).rgb;
vec3 snowColor = texture(snowTexture, inTexCoord * 5.0).rgb;
float sandFactor = smoothstep(0.0, sandThreshold, height);
float grassFactor = smoothstep(sandThreshold, grassThreshold, height);
float rockFactor = smoothstep(grassThreshold, rockThreshold, height);
float snowFactor = smoothstep(rockThreshold, snowThreshold, height);
vec3 finalColor = mix(sandColor, grassColor, sandFactor);
finalColor = mix(finalColor, rockColor, grassFactor);
finalColor = mix(finalColor, snowColor, rockFactor);
Π‘Π»Π΅Π΄ΡΡΡΠΈΠ΅ ΡΠ°Π³ΠΈ
Π’Π΅ΠΏΠ΅ΡΡ, ΠΊΠΎΠ³Π΄Π° Π²Ρ ΠΎΠ·Π½Π°ΠΊΠΎΠΌΠΈΠ»ΠΈΡΡ Ρ ΡΠΈΡΡΠ΅ΠΌΠΎΠΉ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΎΠ² ΠΈ ΡΠ΅ΠΉΠ΄Π΅ΡΠΎΠ² Wudgine, ΡΠ΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡΠ΅ΠΌ: