forked from LeenkxTeam/LNXSDK
Compare commits
12 Commits
2be36398f7
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7aeebf2008 | |||
| 0b4184ccc2 | |||
| ddc12e8607 | |||
| 6ad647ae56 | |||
| b77aca926a | |||
| c2bb20f905 | |||
| 14c6a7be03 | |||
| 85d63e8413 | |||
| 78452aaf67 | |||
| 1f72636350 | |||
| 62433ce86a | |||
| 572665e8e6 |
BIN
Krom/Krom.exe
BIN
Krom/Krom.exe
Binary file not shown.
@ -7,7 +7,7 @@ bl_info = {
|
||||
"description": "Full Stack SDK",
|
||||
"author": "Leenkx.com",
|
||||
"version": (2026, 5, 0),
|
||||
"blender": (4, 5, 0),
|
||||
"blender": (5, 2, 0),
|
||||
"doc_url": "https://leenkx.com/",
|
||||
"tracker_url": "https://leenkx.com/support"
|
||||
}
|
||||
|
||||
@ -21,6 +21,8 @@ in vec3 wnormal;
|
||||
+-------------------+-----------------++--------------+--------------+-----------------+--------------------+
|
||||
| GBUF_IDX_EMISSION | _EmissionShaded || emission color (RGB) | unused |
|
||||
+-------------------+-----------------++--------------+--------------+-----------------+--------------------+
|
||||
| GBUF_IDX_3 | _Anisotropy || world tangent (XYZ) | unused |
|
||||
+-------------------+-----------------++--------------+--------------+-----------------+--------------------+
|
||||
|
||||
The indices as well as the GBUF_SIZE define are defined in "compiled.inc".
|
||||
*/
|
||||
@ -54,4 +56,8 @@ void main() {
|
||||
#ifdef _SSRefraction
|
||||
fragColor[GBUF_IDX_REFRACTION] = vec4(ior, opacity, 0.0, 0.0);
|
||||
#endif
|
||||
|
||||
#ifdef _Anisotropy
|
||||
fragColor[GBUF_IDX_3] = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
#ifdef _SSRS
|
||||
#include "std/ssrs.glsl"
|
||||
#endif
|
||||
#include "std/brdf.glsl"
|
||||
|
||||
uniform sampler2D gbufferD;
|
||||
uniform sampler2D gbuffer0;
|
||||
@ -22,6 +23,9 @@ uniform sampler2D gbuffer1;
|
||||
#ifdef _gbuffer2
|
||||
uniform sampler2D gbuffer2;
|
||||
#endif
|
||||
#ifdef _Anisotropy
|
||||
uniform sampler2D gbuffer3;
|
||||
#endif
|
||||
#ifdef _EmissionShaded
|
||||
uniform sampler2D gbufferEmission;
|
||||
#endif
|
||||
@ -254,6 +258,13 @@ void main() {
|
||||
float metallic;
|
||||
uint matid;
|
||||
unpackFloatInt16(g0.a, metallic, matid);
|
||||
#ifdef _ExtBRDF
|
||||
matid = min(matid, uint(MAX_MATERIALS - 1));
|
||||
|
||||
//!uniform vec4 materialParams[MAX_MATERIALS * 8];
|
||||
vec4 matp0, matp1, matp2, matp3, matp4, matp5, matp6;
|
||||
getMaterialParams(matid, matp0, matp1, matp2, matp3, matp4, matp5, matp6);
|
||||
#endif
|
||||
|
||||
vec2 occspec = unpackFloat2(g1.a);
|
||||
// re-investigate clamp basecolor to prevent extreme values causing glitches
|
||||
@ -283,6 +294,11 @@ void main() {
|
||||
vec4 g2 = textureLod(gbuffer2, texCoord, 0.0);
|
||||
#endif
|
||||
|
||||
#ifdef _Anisotropy
|
||||
vec4 g3 = textureLod(gbuffer3, texCoord, 0.0);
|
||||
vec3 wTangent = length(g3.xyz) > 0.0 ? normalize(g3.xyz) : vec3(1.0, 0.0, 0.0);
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _MicroShadowing
|
||||
occspec.x = mix(1.0, occspec.x, dotNV); // AO Fresnel
|
||||
@ -406,8 +422,50 @@ void main() {
|
||||
float sdotVH = max(0.0, dot(v, sh));
|
||||
float sdotNL = max(0.0, dot(n, sunDir));
|
||||
vec3 svisibility = vec3(1.0);
|
||||
#ifdef _Anisotropy
|
||||
vec3 sdirect;
|
||||
if (abs(matp0.x) > 0.001) {
|
||||
vec3 sbitangent = normalize(cross(n, wTangent));
|
||||
sdirect = lambertDiffuseBRDF(albedo, sdotNL) +
|
||||
anisotropicBRDF(f0, roughness, matp0.x, matp0.y,
|
||||
wTangent, sbitangent, n, sunDir, v, sdotNL, dotNV) * occspec.y;
|
||||
} else {
|
||||
sdirect = lambertDiffuseBRDF(albedo, sdotNL) +
|
||||
specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * occspec.y;
|
||||
}
|
||||
#else
|
||||
vec3 sdirect = lambertDiffuseBRDF(albedo, sdotNL) +
|
||||
specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * occspec.y;
|
||||
#endif
|
||||
|
||||
float sunSheenWeight = 1.0;
|
||||
float sunCoatWeight = 1.0;
|
||||
|
||||
#ifdef _Sheen
|
||||
vec3 sunSheen = sheenBRDF(matp0.z, matp0.w, vec3(matp5.z, matp5.w, matp6.x), sdotNL, sdotNH, dotNV);
|
||||
sunSheenWeight = sheenAttenuation(matp0.z, matp0.w, vec3(matp5.z, matp5.w, matp6.x), dotNV);
|
||||
#endif
|
||||
|
||||
#ifdef _ClearCoat
|
||||
vec3 sunCoat = clearcoatBRDF(matp1.x, matp1.y, matp1.z, sdotNL, sdotNH, dotNV, sdotVH);
|
||||
sunCoatWeight = coatAttenuation(matp1.x, matp1.z, dotNV);
|
||||
#endif
|
||||
|
||||
float sunLayerWeight = sunSheenWeight * sunCoatWeight;
|
||||
sdirect *= sunLayerWeight;
|
||||
#ifdef _Subsurface
|
||||
sdirect += subsurfaceBRDF(albedo, vec3(matp4.w, matp5.x, matp5.y), vec3(matp4.x, matp4.y, matp4.z), matp3.z, matp3.w, sdotNL) * sunLayerWeight;
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
sdirect += transmissionBRDF(albedo, matp2.z, matp2.w, matp3.x, matp3.y, sdotNL, dotNV, sdotVH) * sunLayerWeight;
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
sdirect *= coatTintAttenuation(matp1.x, vec3(matp1.w, matp2.x, matp2.y), dotNV);
|
||||
sdirect += sunCoat * sunSheenWeight;
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
sdirect += sunSheen;
|
||||
#endif
|
||||
|
||||
#ifdef _ShadowMap
|
||||
#ifdef _CSM
|
||||
@ -555,6 +613,21 @@ void main() {
|
||||
#ifdef _SSRS
|
||||
, gbufferD, invVP, eye
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
, matp1.x, matp1.y, matp1.z, vec3(matp1.w, matp2.x, matp2.y)
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
, matp0.z, matp0.w, vec3(matp5.z, matp5.w, matp6.x)
|
||||
#endif
|
||||
#ifdef _Anisotropy
|
||||
, matp0.x, matp0.y, wTangent
|
||||
#endif
|
||||
#ifdef _Subsurface
|
||||
, matp3.z, vec3(matp4.w, matp5.x, matp5.y), vec3(matp4.x, matp4.y, matp4.z), matp3.w
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
, matp2.z, matp2.w, matp3.x, matp3.y
|
||||
#endif
|
||||
);
|
||||
|
||||
#ifdef _Spot
|
||||
@ -618,6 +691,21 @@ void main() {
|
||||
#ifdef _SSRS
|
||||
, gbufferD, invVP, eye
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
, matp1.x, matp1.y, matp1.z, vec3(matp1.w, matp2.x, matp2.y)
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
, matp0.z, matp0.w, vec3(matp5.z, matp5.w, matp6.x)
|
||||
#endif
|
||||
#ifdef _Anisotropy
|
||||
, matp0.x, matp0.y, wTangent
|
||||
#endif
|
||||
#ifdef _Subsurface
|
||||
, matp3.z, vec3(matp4.w, matp5.x, matp5.y), vec3(matp4.x, matp4.y, matp4.z), matp3.w
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
, matp2.z, matp2.w, matp3.x, matp3.y
|
||||
#endif
|
||||
);
|
||||
}
|
||||
#endif // _Clusters
|
||||
|
||||
@ -277,6 +277,11 @@
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpot3",
|
||||
"ifndef": ["_ShadowMapAtlas"],
|
||||
"ifdef": ["_LTC", "_ShadowMap"]
|
||||
},
|
||||
{
|
||||
"name": "materialParams",
|
||||
"link": "_materialParams",
|
||||
"type": "floats"
|
||||
}
|
||||
],
|
||||
"vertex_shader": "../include/pass_viewray.vert.glsl",
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include "compiled.inc"
|
||||
#include "std/gbuffer.glsl"
|
||||
#include "std/math.glsl"
|
||||
#include "std/brdf.glsl"
|
||||
#ifdef _Clusters
|
||||
#include "std/clusters.glsl"
|
||||
#endif
|
||||
@ -13,6 +14,9 @@
|
||||
uniform sampler2D gbufferD;
|
||||
uniform sampler2D gbuffer0;
|
||||
uniform sampler2D gbuffer1;
|
||||
#ifdef _Anisotropy
|
||||
uniform sampler2D gbuffer3;
|
||||
#endif
|
||||
|
||||
uniform float envmapStrength;
|
||||
#ifdef _Irr
|
||||
@ -132,6 +136,13 @@ void main() {
|
||||
float metallic;
|
||||
uint matid;
|
||||
unpackFloatInt16(g0.a, metallic, matid);
|
||||
#ifdef _ExtBRDF
|
||||
matid = min(matid, uint(MAX_MATERIALS - 1));
|
||||
|
||||
//!uniform vec4 materialParams[MAX_MATERIALS * 8];
|
||||
vec4 matp0, matp1, matp2, matp3, matp4, matp5, matp6;
|
||||
getMaterialParams(matid, matp0, matp1, matp2, matp3, matp4, matp5, matp6);
|
||||
#endif
|
||||
|
||||
vec4 g1 = textureLod(gbuffer1, texCoord, 0.0); // Basecolor.rgb, spec/occ
|
||||
vec2 occspec = unpackFloat2(g1.a);
|
||||
@ -143,6 +154,11 @@ void main() {
|
||||
vec3 v = normalize(eye - p);
|
||||
float dotNV = max(dot(n, v), 0.0);
|
||||
|
||||
#ifdef _Anisotropy
|
||||
vec4 g3 = textureLod(gbuffer3, texCoord, 0.0);
|
||||
vec3 wTangent = length(g3.xyz) > 0.0 ? normalize(g3.xyz) : vec3(1.0, 0.0, 0.0);
|
||||
#endif
|
||||
|
||||
#ifdef _Brdf
|
||||
vec2 envBRDF = texelFetch(senvmapBrdf, ivec2(vec2(dotNV, 1.0 - roughness) * 256.0), 0).xy;
|
||||
#endif
|
||||
@ -189,8 +205,50 @@ void main() {
|
||||
float sdotVH = max(0.0, dot(v, sh));
|
||||
float sdotNL = max(0.0, dot(n, sunDir));
|
||||
float svisibility = 1.0;
|
||||
#ifdef _Anisotropy
|
||||
vec3 sdirect;
|
||||
if (abs(matp0.x) > 0.001) {
|
||||
vec3 sbitangent = normalize(cross(n, wTangent));
|
||||
sdirect = lambertDiffuseBRDF(albedo, sdotNL) +
|
||||
anisotropicBRDF(f0, roughness, matp0.x, matp0.y,
|
||||
wTangent, sbitangent, n, sunDir, v, sdotNL, dotNV) * occspec.y;
|
||||
} else {
|
||||
sdirect = lambertDiffuseBRDF(albedo, sdotNL) +
|
||||
specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * occspec.y;
|
||||
}
|
||||
#else
|
||||
vec3 sdirect = lambertDiffuseBRDF(albedo, sdotNL) +
|
||||
specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * occspec.y;
|
||||
#endif
|
||||
|
||||
float sunSheenWeight = 1.0;
|
||||
float sunCoatWeight = 1.0;
|
||||
|
||||
#ifdef _Sheen
|
||||
vec3 sunSheen = sheenBRDF(matp0.z, matp0.w, vec3(matp5.z, matp5.w, matp6.x), sdotNL, sdotNH, dotNV);
|
||||
sunSheenWeight = sheenAttenuation(matp0.z, matp0.w, vec3(matp5.z, matp5.w, matp6.x), dotNV);
|
||||
#endif
|
||||
|
||||
#ifdef _ClearCoat
|
||||
vec3 sunCoat = clearcoatBRDF(matp1.x, matp1.y, matp1.z, sdotNL, sdotNH, dotNV, sdotVH);
|
||||
sunCoatWeight = coatAttenuation(matp1.x, matp1.z, dotNV);
|
||||
#endif
|
||||
|
||||
float sunLayerWeight = sunSheenWeight * sunCoatWeight;
|
||||
sdirect *= sunLayerWeight;
|
||||
#ifdef _Subsurface
|
||||
sdirect += subsurfaceBRDF(albedo, vec3(matp4.w, matp5.x, matp5.y), vec3(matp4.x, matp4.y, matp4.z), matp3.z, matp3.w, sdotNL) * sunLayerWeight;
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
sdirect += transmissionBRDF(albedo, matp2.z, matp2.w, matp3.x, matp3.y, sdotNL, dotNV, sdotVH) * sunLayerWeight;
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
sdirect *= coatTintAttenuation(matp1.x, vec3(matp1.w, matp2.x, matp2.y), dotNV);
|
||||
sdirect += sunCoat * sunSheenWeight;
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
sdirect += sunSheen;
|
||||
#endif
|
||||
|
||||
#ifdef _ShadowMap
|
||||
#ifdef _CSM
|
||||
@ -235,6 +293,21 @@ void main() {
|
||||
#ifdef _Spot
|
||||
, true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight // TODO: Test!
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
, matp1.x, matp1.y, matp1.z, vec3(matp1.w, matp2.x, matp2.y)
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
, matp0.z, matp0.w, vec3(matp5.z, matp5.w, matp6.x)
|
||||
#endif
|
||||
#ifdef _Anisotropy
|
||||
, matp0.x, matp0.y, wTangent
|
||||
#endif
|
||||
#ifdef _Subsurface
|
||||
, matp3.z, vec3(matp4.w, matp5.x, matp5.y), vec3(matp4.x, matp4.y, matp4.z), matp3.w
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
, matp2.z, matp2.w, matp3.x, matp3.y
|
||||
#endif
|
||||
);
|
||||
#endif
|
||||
|
||||
@ -277,6 +350,21 @@ void main() {
|
||||
, vec2(lightsArray[li * 3].w, lightsArray[li * 3 + 1].w) // scale
|
||||
, lightsArraySpot[li * 2 + 1].xyz // right
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
, matp1.x, matp1.y, matp1.z, vec3(matp1.w, matp2.x, matp2.y)
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
, matp0.z, matp0.w, vec3(matp5.z, matp5.w, matp6.x)
|
||||
#endif
|
||||
#ifdef _Anisotropy
|
||||
, matp0.x, matp0.y, wTangent
|
||||
#endif
|
||||
#ifdef _Subsurface
|
||||
, matp3.z, vec3(matp4.w, matp5.x, matp5.y), vec3(matp4.x, matp4.y, matp4.z), matp3.w
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
, matp2.z, matp2.w, matp3.x, matp3.y
|
||||
#endif
|
||||
);
|
||||
}
|
||||
#endif // _Clusters
|
||||
|
||||
@ -214,6 +214,11 @@
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpot3",
|
||||
"ifndef": ["_ShadowMapAtlas"],
|
||||
"ifdef": ["_LTC", "_ShadowMap"]
|
||||
},
|
||||
{
|
||||
"name": "materialParams",
|
||||
"link": "_materialParams",
|
||||
"type": "floats"
|
||||
}
|
||||
],
|
||||
"vertex_shader": "../include/pass_viewray.vert.glsl",
|
||||
|
||||
@ -138,4 +138,137 @@ float D_Approx(const float Roughness, const float RoL) {
|
||||
return rcp_a2 * exp2( c * RoL - c );
|
||||
}
|
||||
|
||||
#ifdef _ClearCoat
|
||||
vec3 clearcoatBRDF(const float clearcoat, const float clearcoat_rough,
|
||||
const float coat_ior,
|
||||
const float dotNL, const float dotNH, const float dotNV, const float dotVH) {
|
||||
if (clearcoat <= 0.0) return vec3(0.0);
|
||||
float a = clearcoat_rough * clearcoat_rough;
|
||||
// F0 from Fresnel equation for dielectric
|
||||
float ccF0 = (coat_ior - 1.0) / (coat_ior + 1.0);
|
||||
ccF0 = ccF0 * ccF0;
|
||||
float F = ccF0 + (1.0 - ccF0) * pow(1.0 - dotVH, 5.0);
|
||||
float D = d_ggx(dotNH, a);
|
||||
float G = g2_approx(dotNL, dotNV, a);
|
||||
return vec3(clearcoat * D * G * F / max(4.0 * dotNV, 1e-5));
|
||||
}
|
||||
|
||||
float coatAttenuation(const float clearcoat,
|
||||
const float coat_ior, const float dotNV) {
|
||||
if (clearcoat <= 0.0) return 1.0;
|
||||
float ccF0 = (coat_ior - 1.0) / (coat_ior + 1.0);
|
||||
ccF0 = ccF0 * ccF0;
|
||||
// Schlick with pow(.,5) - cheaper than exp2
|
||||
float F = ccF0 + (1.0 - ccF0) * pow(1.0 - dotNV, 5.0);
|
||||
return max(1.0 - F * clearcoat, 0.0);
|
||||
}
|
||||
|
||||
vec3 coatTintAttenuation(const float clearcoat, const vec3 coat_tint, const float dotNV) {
|
||||
if (clearcoat <= 0.0) return vec3(1.0);
|
||||
float absorption = 1.0 / max(dotNV, 0.3);
|
||||
return mix(vec3(1.0), coat_tint, clamp(absorption * 0.2, 0.0, 1.0));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _Sheen
|
||||
// based on Blender sheen model/Frostbite PBR
|
||||
vec3 sheenBRDF(const float sheen, const float sheen_rough,
|
||||
const vec3 sheen_tint, const float dotNL, const float dotNH, const float dotNV) {
|
||||
if (sheen <= 0.0) return vec3(0.0);
|
||||
float rough = clamp(sheen_rough, 1e-3, 1.0);
|
||||
float a = rough * rough;
|
||||
float sinNH2 = 1.0 - dotNH * dotNH;
|
||||
float a2 = a * a;
|
||||
float D = (2.0 + a2) * sinNH2 / (2.0 * 3.1415926535 * pow(1.0 + a2 * sinNH2, 2.0));
|
||||
float V = 1.0 / (4.0 * dotNL * dotNV + 1e-5);
|
||||
float sheenAlbedo = (1.0 - 0.5 * rough) * mix(1.0, dotNV, 0.5);
|
||||
return sheen_tint * sheen * D * V * dotNL * sheenAlbedo;
|
||||
}
|
||||
|
||||
float sheenAttenuation(const float sheen, const float sheen_rough,
|
||||
const vec3 sheen_tint, const float dotNV) {
|
||||
if (sheen <= 0.0) return 1.0;
|
||||
float rough = clamp(sheen_rough, 1e-3, 1.0);
|
||||
float sheenAlbedo = (1.0 - 0.5 * rough) * mix(1.0, dotNV, 0.5);
|
||||
float maxComp = sheen * max(max(sheen_tint.r, sheen_tint.g), sheen_tint.b) * sheenAlbedo;
|
||||
return max(1.0 - maxComp, 0.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _Anisotropy
|
||||
// anisotropic GGX Burley 2012
|
||||
vec3 anisotropicBRDF(const vec3 f0, const float roughness, const float anisotropy,
|
||||
const float aniso_rot, const vec3 tangent, const vec3 bitangent,
|
||||
const vec3 n, const vec3 l, const vec3 v,
|
||||
const float dotNL, const float dotNV) {
|
||||
if (abs(anisotropy) <= 0.001) return vec3(0.0);
|
||||
float rot = aniso_rot * 3.1415926535 * 2.0;
|
||||
float cr = cos(rot);
|
||||
float sr = sin(rot);
|
||||
vec3 t = normalize(tangent * cr + bitangent * sr);
|
||||
vec3 b = normalize(bitangent * cr - tangent * sr);
|
||||
float aniso_abs = abs(anisotropy);
|
||||
float at = max(roughness * (1.0 + aniso_abs), 1e-5);
|
||||
float ab = max(roughness * (1.0 - aniso_abs), 1e-5);
|
||||
if (anisotropy < 0.0) { vec3 tmp = t; t = b; b = tmp; }
|
||||
float at2 = at * at;
|
||||
float ab2 = ab * ab;
|
||||
vec3 h = normalize(l + v);
|
||||
float dotTH = dot(t, h);
|
||||
float dotBH = dot(b, h);
|
||||
float dotTV = dot(t, v);
|
||||
float dotBV = dot(b, v);
|
||||
float dotTL = dot(t, l);
|
||||
float dotBL = dot(b, l);
|
||||
float denom = max(dotTH * dotTH / at2 + dotBH * dotBH / ab2, 1e-7);
|
||||
float D = 1.0 / (3.1415926535 * at * ab * denom * denom);
|
||||
float V = 1.0 / max(dotNL * (dotTL / at + dotBL / ab) * (dotTV / at + dotBV / ab), 1e-5);
|
||||
float dotVH = max(dot(v, h), 0.0);
|
||||
vec3 F = f_schlick(f0, dotVH);
|
||||
return D * V * F / max(4.0 * dotNV, 1e-5);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _Subsurface
|
||||
// Blenders bssrdf_burley implementation
|
||||
vec3 subsurfaceBRDF(const vec3 albedo, const vec3 sss_color,
|
||||
const vec3 sss_radius, const float subsurface, const float sss_anisotropy,
|
||||
const float dotNL) {
|
||||
if (subsurface <= 0.0) return vec3(0.0);
|
||||
vec3 mfp = sss_radius * (0.25 / 3.1415926535);
|
||||
vec3 A = clamp(albedo, 0.0, 1.0);
|
||||
vec3 d = 1.9 - A + 3.5 * (A - 0.8) * (A - 0.8);
|
||||
d = mfp / max(d, 1e-5);
|
||||
float aniso = clamp(sss_anisotropy, 0.0, 0.9);
|
||||
float scatter = subsurface * (1.0 / 3.1415926535);
|
||||
vec3 sssDiffuse = sss_color * scatter * dotNL;
|
||||
float backScatter = max(0.0, 1.0 - dotNL) * (1.0 - aniso) * 0.5;
|
||||
vec3 sssBack = sss_color * subsurface * backScatter;
|
||||
float dist = max(0.0, 1.0 - dotNL);
|
||||
vec3 rcp_d = 1.0 / max(d, vec3(1e-5));
|
||||
vec3 x = vec3(dist) * rcp_d;
|
||||
vec3 extinction = 1.0 / (1.0 + x + 0.5 * x * x);
|
||||
return sssDiffuse * extinction + sssBack * (vec3(1.0) - extinction);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _Transmission
|
||||
// Blenders microfacet glass/refraction model
|
||||
vec3 transmissionBRDF(const vec3 albedo, const float transmission,
|
||||
const float trans_rough, const float ior, const float thin_wall,
|
||||
const float dotNL, const float dotNV, const float dotVH) {
|
||||
if (transmission <= 0.0) return vec3(0.0);
|
||||
float F0 = (ior - 1.0) / (ior + 1.0);
|
||||
F0 = F0 * F0;
|
||||
float F = F0 + (1.0 - F0) * pow(1.0 - dotVH, 5.0);
|
||||
float transmittance = 1.0 - F;
|
||||
if (thin_wall > 0.5) {
|
||||
return albedo * transmission * transmittance * dotNL;
|
||||
}
|
||||
float a = trans_rough * trans_rough;
|
||||
float rough_atten = mix(1.0, 1.0 / max(dotNV, 0.1), a);
|
||||
return albedo * transmission * transmittance * rough_atten * dotNL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@ -170,4 +170,19 @@ void unpackFloatInt16(float val, out float f, out uint i) {
|
||||
f = (bitsValue & ~(0xF << numBitFloat)) / maxValFloat;
|
||||
}
|
||||
|
||||
#ifdef _ExtBRDF
|
||||
// extended material parameters by material slot ID returns vec4s (28 floats) of extended BRDF parameters
|
||||
void getMaterialParams(uint matid, out vec4 p0, out vec4 p1, out vec4 p2, out vec4 p3,
|
||||
out vec4 p4, out vec4 p5, out vec4 p6) {
|
||||
uint base = matid * 8u;
|
||||
p0 = materialParams[base];
|
||||
p1 = materialParams[base + 1u];
|
||||
p2 = materialParams[base + 2u];
|
||||
p3 = materialParams[base + 3u];
|
||||
p4 = materialParams[base + 4u];
|
||||
p5 = materialParams[base + 5u];
|
||||
p6 = materialParams[base + 6u];
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@ -131,6 +131,21 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
|
||||
#ifdef _SSRS
|
||||
, sampler2D gbufferD, mat4 invVP, vec3 eye
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
, float clearcoat, float clearcoatRough, float coatIOR, vec3 coatTint
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
, float sheen, float sheenRough, vec3 sheenTint
|
||||
#endif
|
||||
#ifdef _Anisotropy
|
||||
, float anisotropy, float anisoRot, vec3 tangent
|
||||
#endif
|
||||
#ifdef _Subsurface
|
||||
, float subsurface, vec3 sssColor, vec3 sssRadius, float sssAnisotropy
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
, float transmission, float transRough, float ior, float thinWall
|
||||
#endif
|
||||
) {
|
||||
vec3 ld = lp - p;
|
||||
vec3 l = normalize(ld);
|
||||
@ -153,9 +168,49 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
|
||||
float ltcdiff = ltcEvaluate(n, v, dotNV, p, mat3(1.0), lightArea0, lightArea1, lightArea2, lightArea3);
|
||||
vec3 direct = albedo * ltcdiff + ltcspec * spec * 0.05;
|
||||
#else
|
||||
#ifdef _Anisotropy
|
||||
vec3 direct;
|
||||
if (abs(anisotropy) > 0.001) {
|
||||
vec3 bitangent = normalize(cross(n, tangent));
|
||||
direct = lambertDiffuseBRDF(albedo, dotNL) +
|
||||
anisotropicBRDF(f0, rough, anisotropy, anisoRot,
|
||||
tangent, bitangent, n, l, v, dotNL, dotNV) * spec;
|
||||
} else {
|
||||
direct = lambertDiffuseBRDF(albedo, dotNL) +
|
||||
specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec;
|
||||
}
|
||||
#else
|
||||
vec3 direct = lambertDiffuseBRDF(albedo, dotNL) +
|
||||
specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// before attenuate/shadow so everything is properly shadowed in one pass
|
||||
float sheenWeight = 1.0;
|
||||
float coatWeight = 1.0;
|
||||
#ifdef _Sheen
|
||||
vec3 sheenContrib = sheenBRDF(sheen, sheenRough, sheenTint, dotNL, dotNH, dotNV);
|
||||
sheenWeight = sheenAttenuation(sheen, sheenRough, sheenTint, dotNV);
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
vec3 coatContrib = clearcoatBRDF(clearcoat, clearcoatRough, coatIOR, dotNL, dotNH, dotNV, dotVH);
|
||||
coatWeight = coatAttenuation(clearcoat, coatIOR, dotNV);
|
||||
#endif
|
||||
float layerWeight = sheenWeight * coatWeight;
|
||||
direct *= layerWeight;
|
||||
#ifdef _Subsurface
|
||||
direct += subsurfaceBRDF(albedo, sssColor, sssRadius, subsurface, sssAnisotropy, dotNL) * layerWeight;
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
direct += transmissionBRDF(albedo, transmission, transRough, ior, thinWall, dotNL, dotNV, dotVH) * layerWeight;
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
direct *= coatTintAttenuation(clearcoat, coatTint, dotNV);
|
||||
direct += coatContrib * sheenWeight;
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
direct += sheenContrib;
|
||||
#endif
|
||||
|
||||
direct *= attenuate(distance(p, lp));
|
||||
direct *= min(lightCol, vec3(100.0));
|
||||
@ -418,6 +473,21 @@ vec3 sampleLightVoxels(const vec3 p, const vec3 n, const vec3 v, const float dot
|
||||
#ifdef _Spot
|
||||
, const bool isSpot, const float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
, float clearcoat, float clearcoatRough, float coatIOR, vec3 coatTint
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
, float sheen, float sheenRough, vec3 sheenTint
|
||||
#endif
|
||||
#ifdef _Anisotropy
|
||||
, float anisotropy, float anisoRot, vec3 tangent
|
||||
#endif
|
||||
#ifdef _Subsurface
|
||||
, float subsurface, vec3 sssColor, vec3 sssRadius, float sssAnisotropy
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
, float transmission, float transRough, float ior, float thinWall
|
||||
#endif
|
||||
) {
|
||||
vec3 ld = lp - p;
|
||||
vec3 l = normalize(ld);
|
||||
@ -444,6 +514,32 @@ vec3 sampleLightVoxels(const vec3 p, const vec3 n, const vec3 v, const float dot
|
||||
specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec;
|
||||
#endif
|
||||
|
||||
float sheenWeight = 1.0;
|
||||
float coatWeight = 1.0;
|
||||
#ifdef _Sheen
|
||||
vec3 sheenContrib = sheenBRDF(sheen, sheenRough, sheenTint, dotNL, dotNH, dotNV);
|
||||
sheenWeight = sheenAttenuation(sheen, sheenRough, sheenTint, dotNV);
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
vec3 coatContrib = clearcoatBRDF(clearcoat, clearcoatRough, coatIOR, dotNL, dotNH, dotNV, dotVH);
|
||||
coatWeight = coatAttenuation(clearcoat, coatIOR, dotNV);
|
||||
#endif
|
||||
float layerWeight = sheenWeight * coatWeight;
|
||||
direct *= layerWeight;
|
||||
#ifdef _Subsurface
|
||||
direct += subsurfaceBRDF(albedo, sssColor, sssRadius, subsurface, sssAnisotropy, dotNL) * layerWeight;
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
direct += transmissionBRDF(albedo, transmission, transRough, ior, thinWall, dotNL, dotNV, dotVH) * layerWeight;
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
direct *= coatTintAttenuation(clearcoat, coatTint, dotNV);
|
||||
direct += coatContrib * sheenWeight;
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
direct += sheenContrib;
|
||||
#endif
|
||||
|
||||
direct *= attenuate(distance(p, lp));
|
||||
// CRITICAL: Clamp light color to prevent extreme HDR values causing white sphere artifacts
|
||||
direct *= min(lightCol, vec3(100.0));
|
||||
|
||||
@ -53,6 +53,21 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
|
||||
#ifdef _Spot
|
||||
, bool isSpot, float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
, float clearcoat, float clearcoatRough, float coatIOR, vec3 coatTint
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
, float sheen, float sheenRough, vec3 sheenTint
|
||||
#endif
|
||||
#ifdef _Anisotropy
|
||||
, float anisotropy, float anisoRot, vec3 tangent
|
||||
#endif
|
||||
#ifdef _Subsurface
|
||||
, float subsurface, vec3 sssColor, vec3 sssRadius, float sssAnisotropy
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
, float transmission, float transRough, float ior, float thinWall
|
||||
#endif
|
||||
) {
|
||||
vec3 ld = lp - p;
|
||||
vec3 l = normalize(ld);
|
||||
@ -61,8 +76,47 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
|
||||
float dotVH = max(0.0, dot(v, h));
|
||||
float dotNL = max(0.0, dot(n, l));
|
||||
|
||||
#ifdef _Anisotropy
|
||||
vec3 direct;
|
||||
if (abs(anisotropy) > 0.001) {
|
||||
vec3 bitangent = normalize(cross(n, tangent));
|
||||
direct = lambertDiffuseBRDF(albedo, dotNL) +
|
||||
anisotropicBRDF(f0, rough, anisotropy, anisoRot,
|
||||
tangent, bitangent, n, l, v, dotNL, dotNV) * spec;
|
||||
} else {
|
||||
direct = lambertDiffuseBRDF(albedo, dotNL) +
|
||||
specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec;
|
||||
}
|
||||
#else
|
||||
vec3 direct = lambertDiffuseBRDF(albedo, dotNL) +
|
||||
specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec;
|
||||
#endif
|
||||
|
||||
float sheenWeight = 1.0;
|
||||
float coatWeight = 1.0;
|
||||
#ifdef _Sheen
|
||||
vec3 sheenContrib = sheenBRDF(sheen, sheenRough, sheenTint, dotNL, dotNH, dotNV);
|
||||
sheenWeight = sheenAttenuation(sheen, sheenRough, sheenTint, dotNV);
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
vec3 coatContrib = clearcoatBRDF(clearcoat, clearcoatRough, coatIOR, dotNL, dotNH, dotNV, dotVH);
|
||||
coatWeight = coatAttenuation(clearcoat, coatIOR, dotNV);
|
||||
#endif
|
||||
float layerWeight = sheenWeight * coatWeight;
|
||||
direct *= layerWeight;
|
||||
#ifdef _Subsurface
|
||||
direct += subsurfaceBRDF(albedo, sssColor, sssRadius, subsurface, sssAnisotropy, dotNL) * layerWeight;
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
direct += transmissionBRDF(albedo, transmission, transRough, ior, thinWall, dotNL, dotNV, dotVH) * layerWeight;
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
direct *= coatTintAttenuation(clearcoat, coatTint, dotNV);
|
||||
direct += coatContrib * sheenWeight;
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
direct += sheenContrib;
|
||||
#endif
|
||||
|
||||
direct *= lightCol;
|
||||
direct *= attenuate(distance(p, lp));
|
||||
|
||||
@ -71,6 +71,11 @@ class Scene {
|
||||
|
||||
public var embedded: Map<String, kha.Image>;
|
||||
|
||||
#if (rp_renderer == "Deferred")
|
||||
public var materialParamsBuffer: kha.arrays.Float32Array;
|
||||
public var materialParamsDirty: Bool = true;
|
||||
#end
|
||||
|
||||
public var ready: Bool; // Async in progress
|
||||
|
||||
public var traitInits: Array<Void->Void> = [];
|
||||
@ -107,6 +112,9 @@ class Scene {
|
||||
armatures = [];
|
||||
#end
|
||||
embedded = new Map();
|
||||
#if (rp_renderer == "Deferred")
|
||||
materialParamsBuffer = new kha.arrays.Float32Array(512);
|
||||
#end
|
||||
root = new Object();
|
||||
root.name = "Root";
|
||||
traitInits = [];
|
||||
@ -204,6 +212,77 @@ class Scene {
|
||||
root.remove();
|
||||
}
|
||||
|
||||
#if (rp_renderer == "Deferred")
|
||||
public function updateMaterialParams() {
|
||||
if (!materialParamsDirty) return;
|
||||
materialParamsDirty = false;
|
||||
var buf = materialParamsBuffer;
|
||||
for (i in 0...buf.length) buf[i] = 0.0;
|
||||
for (m in meshes) {
|
||||
if (m.materials == null) continue;
|
||||
for (mat in m.materials) {
|
||||
if (mat == null) continue;
|
||||
if (mat.contexts == null) continue;
|
||||
var slot = -1;
|
||||
var bc = null;
|
||||
for (ctx in mat.contexts) {
|
||||
if (ctx == null || ctx.raw == null) continue;
|
||||
if (ctx.raw.name == "mesh" && ctx.raw.bind_constants != null) {
|
||||
bc = ctx.raw.bind_constants;
|
||||
for (c in bc) {
|
||||
if (c != null && c.name == "materialID" && c.intValue != null) {
|
||||
slot = c.intValue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (slot <= 0 || slot >= 16) continue;
|
||||
var base = slot * 32;
|
||||
if (base + 31 >= buf.length) continue;
|
||||
if (bc == null) continue;
|
||||
for (c in bc) {
|
||||
if (c == null || c.name == null) continue;
|
||||
switch (c.name) {
|
||||
// matp0: vec4(anisotropy, anisoRot, sheen, sheenRough)
|
||||
case "anisotropy": buf[base + 0] = c.floatValue;
|
||||
case "anisoRot": buf[base + 1] = c.floatValue;
|
||||
case "sheen": buf[base + 2] = c.floatValue;
|
||||
case "sheenRough": buf[base + 3] = c.floatValue;
|
||||
// matp1: vec4(clearcoat, clearcoatRough, coatIOR, coatTintR)
|
||||
case "clearcoat": buf[base + 4] = c.floatValue;
|
||||
case "clearcoatRough": buf[base + 5] = c.floatValue;
|
||||
case "coatIOR": buf[base + 6] = c.floatValue;
|
||||
case "coatTintR": buf[base + 7] = c.floatValue;
|
||||
// matp2: vec4(coatTintG, coatTintB, transmission, transRough)
|
||||
case "coatTintG": buf[base + 8] = c.floatValue;
|
||||
case "coatTintB": buf[base + 9] = c.floatValue;
|
||||
case "transmission": buf[base + 10] = c.floatValue;
|
||||
case "transmissionRough": buf[base + 11] = c.floatValue;
|
||||
// matp3: vec4(ior, thinWall, subsurface, subsurfaceAnisotropy)
|
||||
case "ior": buf[base + 12] = c.floatValue;
|
||||
case "thinWall": buf[base + 13] = c.floatValue;
|
||||
case "subsurface": buf[base + 14] = c.floatValue;
|
||||
case "subsurfaceAnisotropy": buf[base + 15] = c.floatValue;
|
||||
// matp4: vec4(subsurfaceRadiusR, subsurfaceRadiusG, subsurfaceRadiusB, subsurfaceColorR)
|
||||
case "subsurfaceRadiusR": buf[base + 16] = c.floatValue;
|
||||
case "subsurfaceRadiusG": buf[base + 17] = c.floatValue;
|
||||
case "subsurfaceRadiusB": buf[base + 18] = c.floatValue;
|
||||
case "subsurfaceColorR": buf[base + 19] = c.floatValue;
|
||||
// matp5: vec4(subsurfaceColorG, subsurfaceColorB, sheenTintR, sheenTintG)
|
||||
case "subsurfaceColorG": buf[base + 20] = c.floatValue;
|
||||
case "subsurfaceColorB": buf[base + 21] = c.floatValue;
|
||||
case "sheenTintR": buf[base + 22] = c.floatValue;
|
||||
case "sheenTintG": buf[base + 23] = c.floatValue;
|
||||
// matp6: vec4(sheenTintB, 0, 0, 0)
|
||||
case "sheenTintB": buf[base + 24] = c.floatValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
static var framePassed = true;
|
||||
public static function setActive(sceneName: String, done: Object->Void = null) {
|
||||
if (!framePassed) return;
|
||||
@ -351,6 +430,9 @@ class Scene {
|
||||
public function addMeshObject(data: MeshData, materials: Vector<MaterialData>, parent: Object = null): MeshObject {
|
||||
var object = new MeshObject(data, materials);
|
||||
parent != null ? object.setParent(parent) : object.setParent(root);
|
||||
#if (rp_renderer == "Deferred")
|
||||
materialParamsDirty = true;
|
||||
#end
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
@ -89,6 +89,9 @@ class MeshObject extends Object {
|
||||
#end
|
||||
if (tilesheet != null) tilesheet.remove();
|
||||
if (Scene.active != null) Scene.active.meshes.remove(this);
|
||||
#if (rp_renderer == "Deferred")
|
||||
if (Scene.active != null) Scene.active.materialParamsDirty = true;
|
||||
#end
|
||||
data.refcount--;
|
||||
super.remove();
|
||||
}
|
||||
|
||||
@ -887,6 +887,9 @@ class Uniforms {
|
||||
case "_envmapIrradiance": {
|
||||
fa = Scene.active.world == null ? WorldData.getEmptyIrradiance() : Scene.active.world.probe.irradiance;
|
||||
}
|
||||
case "_materialParams": {
|
||||
fa = Scene.active.materialParamsBuffer;
|
||||
}
|
||||
#if lnx_clusters
|
||||
case "_lightsArray": {
|
||||
fa = LightObject.lightsArray;
|
||||
|
||||
@ -26,7 +26,8 @@ class RenderPathDeferred {
|
||||
"gbuffer1",
|
||||
#if rp_gbuffer2 "gbuffer2", #end
|
||||
#if rp_gbuffer_emission "gbuffer_emission", #end
|
||||
#if (rp_ssrefr || lnx_voxelgi_refract) "gbuffer_refraction" #end
|
||||
#if (rp_ssrefr || lnx_voxelgi_refract) "gbuffer_refraction", #end
|
||||
#if lnx_anisotropy "gbuffer3" #end
|
||||
]);
|
||||
}
|
||||
|
||||
@ -173,6 +174,19 @@ class RenderPathDeferred {
|
||||
}
|
||||
#end
|
||||
|
||||
#if lnx_anisotropy
|
||||
{
|
||||
var t = new RenderTargetRaw();
|
||||
t.name = "gbuffer3";
|
||||
t.width = 0;
|
||||
t.height = 0;
|
||||
t.displayp = Inc.getDisplayp();
|
||||
t.format = "RGBA64";
|
||||
t.scale = Inc.getSuperSampling();
|
||||
path.createRenderTarget(t);
|
||||
}
|
||||
#end
|
||||
|
||||
#if rp_material_solid
|
||||
path.loadShader("shader_datas/deferred_light_solid/deferred_light");
|
||||
#elseif rp_material_mobile
|
||||
@ -566,6 +580,13 @@ class RenderPathDeferred {
|
||||
}
|
||||
#end
|
||||
|
||||
#if lnx_anisotropy
|
||||
{
|
||||
path.setTarget("gbuffer3");
|
||||
path.clearTarget(0x00000000);
|
||||
}
|
||||
#end
|
||||
|
||||
RenderPathCreator.setTargetMeshes();
|
||||
|
||||
#if rp_dynres
|
||||
@ -761,6 +782,10 @@ class RenderPathDeferred {
|
||||
}
|
||||
#end
|
||||
|
||||
#if lnx_anisotropy
|
||||
path.bindTarget("gbuffer3", "gbuffer3");
|
||||
#end
|
||||
|
||||
#if rp_ssao
|
||||
{
|
||||
if (leenkx.data.Config.raw.rp_ssao != false) {
|
||||
@ -826,8 +851,10 @@ class RenderPathDeferred {
|
||||
#if rp_material_solid
|
||||
path.drawShader("shader_datas/deferred_light_solid/deferred_light");
|
||||
#elseif rp_material_mobile
|
||||
Scene.active.updateMaterialParams();
|
||||
path.drawShader("shader_datas/deferred_light_mobile/deferred_light");
|
||||
#else
|
||||
Scene.active.updateMaterialParams();
|
||||
voxelao_pass ?
|
||||
path.drawShader("shader_datas/deferred_light/deferred_light_VoxelAOvar") :
|
||||
path.drawShader("shader_datas/deferred_light/deferred_light");
|
||||
|
||||
@ -2526,6 +2526,9 @@ class LeenkxExporter:
|
||||
decals_used = False
|
||||
sss_used = False
|
||||
|
||||
from lnx.material import mat_state
|
||||
mat_state.next_ext_mat_id = 3
|
||||
|
||||
for material in self.material_array:
|
||||
# If the material is unlinked, material becomes None
|
||||
if material is None:
|
||||
|
||||
@ -136,7 +136,10 @@ def always() -> float:
|
||||
# Force ui redraw
|
||||
if state.redraw_ui:
|
||||
if bpy.context.screen is not None:
|
||||
krom_active = bool(lnx.render_engine._active_krom_engines)
|
||||
for area in bpy.context.screen.areas:
|
||||
if area.type == 'VIEW_3D' and krom_active:
|
||||
continue
|
||||
if area.type in ('NODE_EDITOR', 'PROPERTIES', 'VIEW_3D'):
|
||||
area.tag_redraw()
|
||||
state.redraw_ui = False
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import bpy
|
||||
import importlib
|
||||
import inspect
|
||||
import pkgutil
|
||||
|
||||
@ -804,41 +804,71 @@ def viewport_build_done():
|
||||
_viewport_pending_launches.clear()
|
||||
log.error('Viewport build failed, check console')
|
||||
|
||||
def play_viewport(viewport_id, width=1920, height=1080):
|
||||
"""Launch Krom in a viewport"""
|
||||
global _viewport_build_in_progress, _viewport_pending_launches, _viewport_proc_build
|
||||
def play(viewport_id=None, width=1920, height=1080):
|
||||
"""Launch player external or embedded when viewport_id is passed"""
|
||||
global _viewport_build_in_progress, _viewport_pending_launches, _viewport_proc_build, scripts_mtime
|
||||
|
||||
if not viewport_id:
|
||||
log.error('No viewport_id: play_viewport requires an id')
|
||||
return
|
||||
|
||||
if 'Lnx' not in bpy.data.worlds:
|
||||
if viewport_id and 'Lnx' not in bpy.data.worlds:
|
||||
log.error('No Lnx world found - cannot start viewport server')
|
||||
return
|
||||
|
||||
wrd = bpy.data.worlds['Lnx']
|
||||
target = 'krom' if viewport_id else runtime_to_target()
|
||||
|
||||
krom_js_path = lnx.utils.get_fp_build() + '/debug/krom/krom.js'
|
||||
if os.path.exists(krom_js_path) and not wrd.lnx_recompile:
|
||||
run_viewport_runtime(viewport_id, width, height)
|
||||
return
|
||||
khajs_path = get_khajs_path(target)
|
||||
if not wrd.lnx_cache_build or \
|
||||
not os.path.isfile(khajs_path) or \
|
||||
assets.khafile_defs_last != assets.khafile_defs or \
|
||||
state.last_target != state.target:
|
||||
wrd.lnx_recompile = True
|
||||
|
||||
pending_entry = (viewport_id, width, height)
|
||||
if pending_entry not in _viewport_pending_launches:
|
||||
_viewport_pending_launches.append(pending_entry)
|
||||
state.last_target = state.target
|
||||
|
||||
if _viewport_build_in_progress:
|
||||
if _viewport_proc_build is not None and _viewport_proc_build.poll() is None:
|
||||
log.info(f'Build in progress, viewport {viewport_id} will launch when ready')
|
||||
state.mod_scripts = []
|
||||
script_path = lnx.utils.get_fp() + '/Sources/' + lnx.utils.safestr(wrd.lnx_project_package)
|
||||
if os.path.isdir(script_path):
|
||||
new_mtime = scripts_mtime
|
||||
for fn in glob.iglob(os.path.join(script_path, '**', '*.hx'), recursive=True):
|
||||
mtime = os.path.getmtime(fn)
|
||||
if scripts_mtime < mtime:
|
||||
lnx.utils.fetch_script_props(fn)
|
||||
fn = fn.split('Sources/')[1]
|
||||
fn = fn[:-3]
|
||||
fn = fn.replace('/', '.')
|
||||
state.mod_scripts.append(fn)
|
||||
wrd.lnx_recompile = True
|
||||
if new_mtime < mtime:
|
||||
new_mtime = mtime
|
||||
scripts_mtime = new_mtime
|
||||
if len(state.mod_scripts) > 0:
|
||||
lnx.utils.fetch_trait_props()
|
||||
|
||||
if viewport_id:
|
||||
krom_js_path = lnx.utils.get_fp_build() + '/debug/krom/krom.js'
|
||||
if os.path.exists(krom_js_path) and not wrd.lnx_recompile:
|
||||
run_viewport_runtime(viewport_id, width, height)
|
||||
return
|
||||
else:
|
||||
_viewport_build_in_progress = False
|
||||
|
||||
_viewport_build_in_progress = True
|
||||
pending_entry = (viewport_id, width, height)
|
||||
if pending_entry not in _viewport_pending_launches:
|
||||
_viewport_pending_launches.append(pending_entry)
|
||||
|
||||
build(target='krom', is_viewport=True, viewport_width=width, viewport_height=height)
|
||||
if _viewport_build_in_progress:
|
||||
if _viewport_proc_build is not None and _viewport_proc_build.poll() is None:
|
||||
log.info(f'Build in progress, viewport {viewport_id} will launch when ready')
|
||||
return
|
||||
else:
|
||||
_viewport_build_in_progress = False
|
||||
|
||||
compile_viewport(assets_only=(not wrd.lnx_recompile))
|
||||
_viewport_build_in_progress = True
|
||||
|
||||
assets.invalidate_enabled = False
|
||||
build(target='krom', is_viewport=True, viewport_width=width, viewport_height=height)
|
||||
compile_viewport(assets_only=(not wrd.lnx_recompile))
|
||||
assets.invalidate_enabled = True
|
||||
else:
|
||||
build(target=target, is_play=True)
|
||||
compile(assets_only=(not wrd.lnx_recompile))
|
||||
|
||||
|
||||
def stop_viewport(viewport_id=None):
|
||||
@ -905,43 +935,6 @@ def get_khajs_path(target):
|
||||
return lnx.utils.build_dir() + '/debug/krom/krom.js'
|
||||
return lnx.utils.build_dir() + '/debug/html5/kha.js'
|
||||
|
||||
def play():
|
||||
global scripts_mtime
|
||||
wrd = bpy.data.worlds['Lnx']
|
||||
|
||||
build(target=runtime_to_target(), is_play=True)
|
||||
|
||||
khajs_path = get_khajs_path(state.target)
|
||||
if not wrd.lnx_cache_build or \
|
||||
not os.path.isfile(khajs_path) or \
|
||||
assets.khafile_defs_last != assets.khafile_defs or \
|
||||
state.last_target != state.target:
|
||||
wrd.lnx_recompile = True
|
||||
|
||||
state.last_target = state.target
|
||||
|
||||
# Trait sources modified
|
||||
state.mod_scripts = []
|
||||
script_path = lnx.utils.get_fp() + '/Sources/' + lnx.utils.safestr(wrd.lnx_project_package)
|
||||
if os.path.isdir(script_path):
|
||||
new_mtime = scripts_mtime
|
||||
for fn in glob.iglob(os.path.join(script_path, '**', '*.hx'), recursive=True):
|
||||
mtime = os.path.getmtime(fn)
|
||||
if scripts_mtime < mtime:
|
||||
lnx.utils.fetch_script_props(fn) # Trait props
|
||||
fn = fn.split('Sources/')[1]
|
||||
fn = fn[:-3] #.hx
|
||||
fn = fn.replace('/', '.')
|
||||
state.mod_scripts.append(fn)
|
||||
wrd.lnx_recompile = True
|
||||
if new_mtime < mtime:
|
||||
new_mtime = mtime
|
||||
scripts_mtime = new_mtime
|
||||
if len(state.mod_scripts) > 0: # Trait props
|
||||
lnx.utils.fetch_trait_props()
|
||||
|
||||
compile(assets_only=(not wrd.lnx_recompile))
|
||||
|
||||
def build_success():
|
||||
log.clear()
|
||||
wrd = bpy.data.worlds['Lnx']
|
||||
|
||||
@ -173,6 +173,9 @@ def add_world_defs():
|
||||
wrd.world_defs += '_Brdf'
|
||||
assets.add_khafile_def("lnx_brdf")
|
||||
|
||||
if '_Anisotropy' in wrd.world_defs:
|
||||
assets.add_khafile_def('lnx_anisotropy')
|
||||
|
||||
def build():
|
||||
rpdat = lnx.utils.get_rp()
|
||||
project_path = lnx.utils.get_fp()
|
||||
@ -510,4 +513,8 @@ def get_num_gbuffer_rts_deferred()-> int:
|
||||
found_refraction_flag = True
|
||||
else:
|
||||
num += 1
|
||||
|
||||
if '_Anisotropy' in wrd.world_defs:
|
||||
num += 1
|
||||
|
||||
return num
|
||||
|
||||
@ -188,6 +188,64 @@ def parse(nodes, con: ShaderContext,
|
||||
|
||||
# Make sure that individual functions in this module aren't called with an incorrect/old parser state, set it to
|
||||
# None so that it will raise exceptions when not set
|
||||
# store extended BRDF feature flags before destroying parser state
|
||||
import re as _re
|
||||
|
||||
def _extract_vec3(s):
|
||||
m = _re.match(r'vec3\s*\(\s*([^,\s\)]+)\s*\)', s)
|
||||
if m and ',' not in m.group(1):
|
||||
v = m.group(1)
|
||||
return v, v, v
|
||||
m = _re.match(r'vec3\s*\(\s*([^,\s]+)\s*,\s*([^,\s]+)\s*,\s*([^,\s\)]+)', s)
|
||||
if m:
|
||||
return m.group(1), m.group(2), m.group(3)
|
||||
return '1.0', '1.0', '1.0'
|
||||
|
||||
def _try_float(val_str, default=0.0):
|
||||
try:
|
||||
return float(val_str)
|
||||
except (ValueError, TypeError):
|
||||
return default
|
||||
|
||||
_sss_r, _sss_g, _sss_b = _extract_vec3(state.out_subsurface_radius)
|
||||
_sss_scale = state.out_subsurface_scale
|
||||
_sss_scale_f = _try_float(_sss_scale, 0.05)
|
||||
if _sss_scale_f != 1.0:
|
||||
_sss_r = str(_try_float(_sss_r, 1.0) * _sss_scale_f)
|
||||
_sss_g = str(_try_float(_sss_g, 0.2) * _sss_scale_f)
|
||||
_sss_b = str(_try_float(_sss_b, 0.1) * _sss_scale_f)
|
||||
|
||||
_sheen_tint_r, _sheen_tint_g, _sheen_tint_b = _extract_vec3(state.out_sheen_tint)
|
||||
_coat_tint_r, _coat_tint_g, _coat_tint_b = _extract_vec3(state.out_coat_tint)
|
||||
_sss_color_r, _sss_color_g, _sss_color_b = _extract_vec3(state.out_subsurface_color)
|
||||
|
||||
mat_state.features = {
|
||||
'clearcoat': state.out_clearcoat,
|
||||
'clearcoatRough': state.out_clearcoat_rough,
|
||||
'coatIOR': state.out_coat_ior,
|
||||
'coatTintR': _coat_tint_r,
|
||||
'coatTintG': _coat_tint_g,
|
||||
'coatTintB': _coat_tint_b,
|
||||
'sheen': state.out_sheen,
|
||||
'sheenRough': state.out_sheen_rough,
|
||||
'sheenTintR': _sheen_tint_r,
|
||||
'sheenTintG': _sheen_tint_g,
|
||||
'sheenTintB': _sheen_tint_b,
|
||||
'subsurface': state.out_subsurface,
|
||||
'subsurfaceAnisotropy': state.out_subsurface_anisotropy,
|
||||
'subsurfaceRadiusR': _sss_r,
|
||||
'subsurfaceRadiusG': _sss_g,
|
||||
'subsurfaceRadiusB': _sss_b,
|
||||
'subsurfaceColorR': _sss_color_r,
|
||||
'subsurfaceColorG': _sss_color_g,
|
||||
'subsurfaceColorB': _sss_color_b,
|
||||
'anisotropy': state.out_anisotropy,
|
||||
'anisoRot': state.out_aniso_rot,
|
||||
'transmission': state.out_transmission,
|
||||
'transmissionRough': state.out_transmission_rough,
|
||||
'ior': state.out_ior,
|
||||
'thinWall': state.out_thin_wall,
|
||||
}
|
||||
state = None
|
||||
|
||||
|
||||
@ -219,7 +277,15 @@ def parse_material_output(node: bpy.types.Node, custom_particle_node: bpy.types.
|
||||
curshader = state.frag
|
||||
state.curshader = curshader
|
||||
|
||||
out_basecol, out_roughness, out_metallic, out_occlusion, out_specular, out_opacity, out_ior, out_emission_col = parse_shader_input(node.inputs[0])
|
||||
outs = parse_shader_input(node.inputs[0])
|
||||
out_basecol = outs[0]
|
||||
out_roughness = outs[1]
|
||||
out_metallic = outs[2]
|
||||
out_occlusion = outs[3]
|
||||
out_specular = outs[4]
|
||||
out_opacity = outs[5]
|
||||
out_ior = outs[6]
|
||||
out_emission_col = outs[7]
|
||||
if parse_surface:
|
||||
curshader.write(f'basecol = {out_basecol};')
|
||||
curshader.write(f'roughness = {out_roughness};')
|
||||
@ -227,6 +293,25 @@ def parse_material_output(node: bpy.types.Node, custom_particle_node: bpy.types.
|
||||
curshader.write(f'occlusion = {out_occlusion};')
|
||||
curshader.write(f'specular = {out_specular};')
|
||||
curshader.write(f'emissionCol = {out_emission_col};')
|
||||
curshader.write(f'subsurface = {outs[8]};')
|
||||
curshader.write(f'subsurfaceRadius = {outs[9]};')
|
||||
curshader.write(f'subsurfaceColor = {outs[10]};')
|
||||
curshader.write(f'specularTint = {outs[11]};')
|
||||
curshader.write(f'anisotropy = {outs[12]};')
|
||||
curshader.write(f'anisoRot = {outs[13]};')
|
||||
curshader.write(f'sheen = {outs[14]};')
|
||||
curshader.write(f'sheenRough = {outs[15]};')
|
||||
curshader.write(f'sheenTint = {outs[16]};')
|
||||
curshader.write(f'clearcoat = {outs[17]};')
|
||||
curshader.write(f'clearcoatRough = {outs[18]};')
|
||||
curshader.write(f'transmission = {outs[19]};')
|
||||
curshader.write(f'transmissionRough = {outs[20]};')
|
||||
curshader.write(f'thinWall = {outs[21]};')
|
||||
curshader.write(f'tangent = {outs[22]};')
|
||||
curshader.write(f'subsurfaceScale = {outs[23]};')
|
||||
curshader.write(f'subsurfaceAnisotropy = {outs[24]};')
|
||||
curshader.write(f'coatIOR = {outs[25]};')
|
||||
curshader.write(f'coatTint = {outs[26]};')
|
||||
|
||||
if mat_state.emission_type == mat_state.EmissionType.SHADELESS:
|
||||
if '_EmissionShadeless' not in wrd.world_defs:
|
||||
@ -330,6 +415,10 @@ def parse_shader(node: bpy.types.Node, socket: bpy.types.NodeSocket) -> Tuple[st
|
||||
'BSDF_GLASS',
|
||||
'HOLDOUT',
|
||||
'SUBSURFACE_SCATTERING',
|
||||
'BSDF_REFRACTION',
|
||||
'BSDF_TOON',
|
||||
'BSDF_HAIR',
|
||||
'BSDF_HAIR_PRINCIPLED',
|
||||
'BSDF_TRANSLUCENT',
|
||||
'BSDF_TRANSPARENT',
|
||||
'BSDF_VELVET',
|
||||
|
||||
@ -16,19 +16,61 @@ if lnx.is_reload(__name__):
|
||||
else:
|
||||
lnx.enable_reload(__name__)
|
||||
|
||||
# Principled BSDF socket names that differ between Blender versions
|
||||
if bpy.app.version < (4, 0, 0):
|
||||
EMISSION_COLOR = 'Emission'
|
||||
SUBSURFACE = 'Subsurface'
|
||||
SUBSURFACE_RADIUS = 'Subsurface Radius'
|
||||
SUBSURFACE_COLOR = 'Subsurface Color'
|
||||
SPECULAR = 'Specular'
|
||||
SPECULAR_TINT = 'Specular Tint'
|
||||
ANISOTROPIC = 'Anisotropic'
|
||||
ANISOTROPIC_ROT = 'Anisotropic Rotation'
|
||||
SHEEN = 'Sheen'
|
||||
SHEEN_TINT = 'Sheen Tint'
|
||||
CLEARCOAT = 'Clearcoat'
|
||||
CLEARCOAT_ROUGHNESS = 'Clearcoat Roughness'
|
||||
CLEARCOAT_NORMAL = 'Clearcoat Normal'
|
||||
TRANSMISSION = 'Transmission'
|
||||
TRANSMISSION_ROUGHNESS = 'Transmission Roughness'
|
||||
TANGENT = 'Tangent'
|
||||
else:
|
||||
EMISSION_COLOR = 'Emission Color'
|
||||
SUBSURFACE = 'Subsurface Weight'
|
||||
SUBSURFACE_RADIUS = 'Subsurface Radius'
|
||||
SUBSURFACE_COLOR = 'Subsurface Color'
|
||||
SPECULAR = 'Specular IOR Level'
|
||||
SPECULAR_TINT = 'Specular Tint'
|
||||
ANISOTROPIC = 'Anisotropic'
|
||||
ANISOTROPIC_ROT = 'Anisotropic Rotation'
|
||||
SHEEN = 'Sheen Weight'
|
||||
SHEEN_TINT = 'Sheen Tint'
|
||||
SHEEN_ROUGHNESS = 'Sheen Roughness'
|
||||
CLEARCOAT = 'Coat Weight'
|
||||
CLEARCOAT_ROUGHNESS = 'Coat Roughness'
|
||||
CLEARCOAT_NORMAL = 'Coat Normal'
|
||||
COAT_IOR = 'Coat IOR'
|
||||
COAT_TINT = 'Coat Tint'
|
||||
TRANSMISSION = 'Transmission Weight'
|
||||
TRANSMISSION_ROUGHNESS = 'Transmission Roughness'
|
||||
TANGENT = 'Tangent'
|
||||
THIN_WALL = 'Thin Wall'
|
||||
SUBSURFACE_SCALE = 'Subsurface Scale'
|
||||
SUBSURFACE_ANISOTROPY = 'Subsurface Anisotropy'
|
||||
|
||||
|
||||
def parse_mixshader(node: bpy.types.ShaderNodeMixShader, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
# Skip mixing if only one input is effectively used
|
||||
if not node.inputs[0].is_linked:
|
||||
if node.inputs[0].default_value <= 0.0:
|
||||
if not node.inputs['Fac'].is_linked:
|
||||
if node.inputs['Fac'].default_value <= 0.0:
|
||||
c.parse_shader_input(node.inputs[1])
|
||||
return
|
||||
elif node.inputs[0].default_value >= 1.0:
|
||||
elif node.inputs['Fac'].default_value >= 1.0:
|
||||
c.parse_shader_input(node.inputs[2])
|
||||
return
|
||||
|
||||
prefix = '' if node.inputs[0].is_linked else 'const '
|
||||
fac = c.parse_value_input(node.inputs[0])
|
||||
prefix = '' if node.inputs['Fac'].is_linked else 'const '
|
||||
fac = c.parse_value_input(node.inputs['Fac'])
|
||||
fac_var = c.node_name(node.name) + '_fac' + state.get_parser_pass_suffix()
|
||||
fac_inv_var = c.node_name(node.name) + '_fac_inv'
|
||||
state.curshader.write('{0}float {1} = clamp({2}, 0.0, 1.0);'.format(prefix, fac_var, fac))
|
||||
@ -36,134 +78,209 @@ def parse_mixshader(node: bpy.types.ShaderNodeMixShader, out_socket: NodeSocket,
|
||||
|
||||
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
||||
sss_before_1 = mat_state.needs_sss
|
||||
bc1, rough1, met1, occ1, spec1, opac1, ior1, emi1 = c.parse_shader_input(node.inputs[1])
|
||||
o1 = c.parse_shader_input(node.inputs[1])
|
||||
sss_1 = mat_state.needs_sss
|
||||
ek1 = mat_state.emission_type
|
||||
|
||||
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
||||
mat_state.needs_sss = sss_before_1 # Reset to state before parsing input 1
|
||||
bc2, rough2, met2, occ2, spec2, opac2, ior2, emi2 = c.parse_shader_input(node.inputs[2])
|
||||
o2 = c.parse_shader_input(node.inputs[2])
|
||||
sss_2 = mat_state.needs_sss
|
||||
ek2 = mat_state.emission_type
|
||||
mat_state.needs_sss = sss_1 or sss_2
|
||||
|
||||
if state.parse_surface:
|
||||
state.out_basecol = '({0} * {3} + {1} * {2})'.format(bc1, bc2, fac_var, fac_inv_var)
|
||||
state.out_roughness = '({0} * {3} + {1} * {2})'.format(rough1, rough2, fac_var, fac_inv_var)
|
||||
state.out_metallic = '({0} * {3} + {1} * {2})'.format(met1, met2, fac_var, fac_inv_var)
|
||||
state.out_occlusion = '({0} * {3} + {1} * {2})'.format(occ1, occ2, fac_var, fac_inv_var)
|
||||
state.out_specular = '({0} * {3} + {1} * {2})'.format(spec1, spec2, fac_var, fac_inv_var)
|
||||
state.out_emission_col = '({0} * {3} + {1} * {2})'.format(emi1, emi2, fac_var, fac_inv_var)
|
||||
fm = '{0} * {3} + {1} * {2}' # fac mix template: a*fac_inv + b*fac
|
||||
fv = (fac_var, fac_inv_var)
|
||||
state.out_basecol = fm.format(o1[0], o2[0], fv[0], fv[1])
|
||||
state.out_roughness = fm.format(o1[1], o2[1], fv[0], fv[1])
|
||||
state.out_metallic = fm.format(o1[2], o2[2], fv[0], fv[1])
|
||||
state.out_occlusion = fm.format(o1[3], o2[3], fv[0], fv[1])
|
||||
state.out_specular = fm.format(o1[4], o2[4], fv[0], fv[1])
|
||||
state.out_emission_col = fm.format(o1[7], o2[7], fv[0], fv[1])
|
||||
state.out_subsurface = fm.format(o1[8], o2[8], fv[0], fv[1])
|
||||
state.out_subsurface_radius = fm.format(o1[9], o2[9], fv[0], fv[1])
|
||||
state.out_subsurface_color = fm.format(o1[10], o2[10], fv[0], fv[1])
|
||||
state.out_specular_tint = fm.format(o1[11], o2[11], fv[0], fv[1])
|
||||
state.out_anisotropy = fm.format(o1[12], o2[12], fv[0], fv[1])
|
||||
state.out_aniso_rot = fm.format(o1[13], o2[13], fv[0], fv[1])
|
||||
state.out_sheen = fm.format(o1[14], o2[14], fv[0], fv[1])
|
||||
state.out_sheen_rough = fm.format(o1[15], o2[15], fv[0], fv[1])
|
||||
state.out_sheen_tint = fm.format(o1[16], o2[16], fv[0], fv[1])
|
||||
state.out_clearcoat = fm.format(o1[17], o2[17], fv[0], fv[1])
|
||||
state.out_clearcoat_rough = fm.format(o1[18], o2[18], fv[0], fv[1])
|
||||
state.out_transmission = fm.format(o1[19], o2[19], fv[0], fv[1])
|
||||
state.out_transmission_rough = fm.format(o1[20], o2[20], fv[0], fv[1])
|
||||
state.out_thin_wall = fm.format(o1[21], o2[21], fv[0], fv[1])
|
||||
state.out_tangent = fm.format(o1[22], o2[22], fv[0], fv[1])
|
||||
state.out_subsurface_scale = fm.format(o1[23], o2[23], fv[0], fv[1])
|
||||
state.out_subsurface_anisotropy = fm.format(o1[24], o2[24], fv[0], fv[1])
|
||||
state.out_coat_ior = fm.format(o1[25], o2[25], fv[0], fv[1])
|
||||
state.out_coat_tint = fm.format(o1[26], o2[26], fv[0], fv[1])
|
||||
mat_state.emission_type = mat_state.EmissionType.get_effective_combination(ek1, ek2)
|
||||
if state.parse_opacity:
|
||||
state.out_opacity = '({0} * {3} + {1} * {2})'.format(opac1, opac2, fac_var, fac_inv_var)
|
||||
state.out_ior = '({0} * {3} + {1} * {2})'.format(ior1, ior2, fac_var, fac_inv_var)
|
||||
fm = '{0} * {3} + {1} * {2}'
|
||||
fv = (fac_var, fac_inv_var)
|
||||
state.out_opacity = fm.format(o1[5], o2[5], fv[0], fv[1])
|
||||
state.out_ior = fm.format(o1[6], o2[6], fv[0], fv[1])
|
||||
|
||||
def parse_addshader(node: bpy.types.ShaderNodeAddShader, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
||||
sss_before_1 = mat_state.needs_sss
|
||||
bc1, rough1, met1, occ1, spec1, opac1, ior1, emi1 = c.parse_shader_input(node.inputs[0])
|
||||
o1 = c.parse_shader_input(node.inputs[0])
|
||||
sss_1 = mat_state.needs_sss
|
||||
ek1 = mat_state.emission_type
|
||||
|
||||
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
||||
mat_state.needs_sss = sss_before_1 # Reset to state before parsing input 0
|
||||
bc2, rough2, met2, occ2, spec2, opac2, ior2, emi2 = c.parse_shader_input(node.inputs[1])
|
||||
o2 = c.parse_shader_input(node.inputs[1])
|
||||
sss_2 = mat_state.needs_sss
|
||||
ek2 = mat_state.emission_type
|
||||
mat_state.needs_sss = sss_1 or sss_2
|
||||
|
||||
if state.parse_surface:
|
||||
state.out_basecol = '({0} + {1})'.format(bc1, bc2)
|
||||
state.out_roughness = '({0} * 0.5 + {1} * 0.5)'.format(rough1, rough2)
|
||||
state.out_metallic = '({0} * 0.5 + {1} * 0.5)'.format(met1, met2)
|
||||
state.out_occlusion = '({0} * 0.5 + {1} * 0.5)'.format(occ1, occ2)
|
||||
state.out_specular = '({0} * 0.5 + {1} * 0.5)'.format(spec1, spec2)
|
||||
state.out_emission_col = '({0} + {1})'.format(emi1, emi2)
|
||||
am = '{0} * 0.5 + {1} * 0.5' # add mix template (average)
|
||||
state.out_basecol = '({0} + {1})'.format(o1[0], o2[0])
|
||||
state.out_roughness = am.format(o1[1], o2[1])
|
||||
state.out_metallic = am.format(o1[2], o2[2])
|
||||
state.out_occlusion = am.format(o1[3], o2[3])
|
||||
state.out_specular = am.format(o1[4], o2[4])
|
||||
state.out_emission_col = '({0} + {1})'.format(o1[7], o2[7])
|
||||
state.out_subsurface = am.format(o1[8], o2[8])
|
||||
state.out_subsurface_radius = am.format(o1[9], o2[9])
|
||||
state.out_subsurface_color = am.format(o1[10], o2[10])
|
||||
state.out_specular_tint = am.format(o1[11], o2[11])
|
||||
state.out_anisotropy = am.format(o1[12], o2[12])
|
||||
state.out_aniso_rot = am.format(o1[13], o2[13])
|
||||
state.out_sheen = am.format(o1[14], o2[14])
|
||||
state.out_sheen_rough = am.format(o1[15], o2[15])
|
||||
state.out_sheen_tint = am.format(o1[16], o2[16])
|
||||
state.out_clearcoat = am.format(o1[17], o2[17])
|
||||
state.out_clearcoat_rough = am.format(o1[18], o2[18])
|
||||
state.out_transmission = am.format(o1[19], o2[19])
|
||||
state.out_transmission_rough = am.format(o1[20], o2[20])
|
||||
state.out_thin_wall = am.format(o1[21], o2[21])
|
||||
state.out_tangent = am.format(o1[22], o2[22])
|
||||
state.out_subsurface_scale = am.format(o1[23], o2[23])
|
||||
state.out_subsurface_anisotropy = am.format(o1[24], o2[24])
|
||||
state.out_coat_ior = am.format(o1[25], o2[25])
|
||||
state.out_coat_tint = am.format(o1[26], o2[26])
|
||||
mat_state.emission_type = mat_state.EmissionType.get_effective_combination(ek1, ek2)
|
||||
if state.parse_opacity:
|
||||
state.out_opacity = '({0} * 0.5 + {1} * 0.5)'.format(opac1, opac2)
|
||||
state.out_ior = '({0} * 0.5 + {1} * 0.5)'.format(ior1, ior2)
|
||||
am = '{0} * 0.5 + {1} * 0.5'
|
||||
state.out_opacity = am.format(o1[5], o2[5])
|
||||
state.out_ior = am.format(o1[6], o2[6])
|
||||
|
||||
# TODO: Refactor using c.get_*_input()
|
||||
|
||||
|
||||
if bpy.app.version < (2, 92, 0):
|
||||
if bpy.app.version < (2, 91, 0):
|
||||
def parse_bsdfprincipled(node: bpy.types.ShaderNodeBsdfPrincipled, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
if state.parse_surface:
|
||||
c.write_normal(node.inputs[20])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
||||
if node.inputs[1].is_linked or node.inputs[1].default_value > 0.0:
|
||||
c.write_normal(node.inputs['Normal'])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs['Base Color'])
|
||||
if node.inputs[SUBSURFACE].is_linked or node.inputs[SUBSURFACE].default_value > 0.0:
|
||||
mat_state.needs_sss = True
|
||||
state.out_metallic = c.parse_value_input(node.inputs[4])
|
||||
state.out_specular = c.parse_value_input(node.inputs[5])
|
||||
state.out_roughness = c.parse_value_input(node.inputs[7])
|
||||
if node.inputs['Emission'].is_linked or not mat_utils.equals_color_socket(node.inputs['Emission'], (0.0, 0.0, 0.0), comp_alpha=False):
|
||||
emission_col = c.parse_vector_input(node.inputs[17])
|
||||
state.out_subsurface = c.parse_value_input(node.inputs[SUBSURFACE])
|
||||
state.out_subsurface_radius = c.parse_vector_input(node.inputs[SUBSURFACE_RADIUS])
|
||||
state.out_subsurface_color = c.parse_vector_input(node.inputs[SUBSURFACE_COLOR])
|
||||
state.out_metallic = c.parse_value_input(node.inputs['Metallic'])
|
||||
state.out_specular = c.parse_value_input(node.inputs[SPECULAR])
|
||||
state.out_specular_tint = 'vec3({})'.format(c.parse_value_input(node.inputs[SPECULAR_TINT]))
|
||||
state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
|
||||
state.out_anisotropy = c.parse_value_input(node.inputs[ANISOTROPIC])
|
||||
state.out_aniso_rot = c.parse_value_input(node.inputs[ANISOTROPIC_ROT])
|
||||
state.out_sheen = c.parse_value_input(node.inputs[SHEEN])
|
||||
state.out_sheen_tint = 'vec3({})'.format(c.parse_value_input(node.inputs[SHEEN_TINT]))
|
||||
state.out_clearcoat = c.parse_value_input(node.inputs[CLEARCOAT])
|
||||
state.out_clearcoat_rough = c.parse_value_input(node.inputs[CLEARCOAT_ROUGHNESS])
|
||||
state.out_transmission = c.parse_value_input(node.inputs[TRANSMISSION])
|
||||
trans_rough_socket = node.inputs.get(TRANSMISSION_ROUGHNESS)
|
||||
if trans_rough_socket is not None:
|
||||
state.out_transmission_rough = c.parse_value_input(trans_rough_socket)
|
||||
state.out_tangent = c.parse_vector_input(node.inputs[TANGENT])
|
||||
if node.inputs[EMISSION_COLOR].is_linked or not mat_utils.equals_color_socket(node.inputs[EMISSION_COLOR], (0.0, 0.0, 0.0), comp_alpha=False):
|
||||
emission_col = c.parse_vector_input(node.inputs[EMISSION_COLOR])
|
||||
state.out_emission_col = emission_col
|
||||
mat_state.emission_type = mat_state.EmissionType.SHADED
|
||||
else:
|
||||
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
||||
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
||||
if state.parse_opacity:
|
||||
state.out_ior = c.parse_value_input(node.inputs[14])
|
||||
# In Blender 2.83, Alpha socket is at index 18, not 19
|
||||
state.out_ior = c.parse_value_input(node.inputs['IOR'])
|
||||
if 'Alpha' in node.inputs:
|
||||
state.out_opacity = c.parse_value_input(node.inputs['Alpha'])
|
||||
else:
|
||||
state.out_opacity = '1.0'
|
||||
if bpy.app.version >= (2, 92, 0) and bpy.app.version <= (4, 1, 0):
|
||||
if bpy.app.version >= (2, 91, 0) and bpy.app.version < (4, 0, 0):
|
||||
def parse_bsdfprincipled(node: bpy.types.ShaderNodeBsdfPrincipled, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
if state.parse_surface:
|
||||
c.write_normal(node.inputs[22])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
||||
if node.inputs[1].is_linked or node.inputs[1].default_value > 0.0:
|
||||
c.write_normal(node.inputs['Normal'])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs['Base Color'])
|
||||
if node.inputs[SUBSURFACE].is_linked or node.inputs[SUBSURFACE].default_value > 0.0:
|
||||
mat_state.needs_sss = True
|
||||
# subsurface_radius = c.parse_vector_input(node.inputs[2])
|
||||
# subsurface_color = c.parse_vector_input(node.inputs[3])
|
||||
state.out_metallic = c.parse_value_input(node.inputs[6])
|
||||
state.out_specular = c.parse_value_input(node.inputs[7])
|
||||
# specular_tint = c.parse_vector_input(node.inputs[6])
|
||||
state.out_roughness = c.parse_value_input(node.inputs[9])
|
||||
# aniso = c.parse_vector_input(node.inputs[8])
|
||||
# aniso_rot = c.parse_vector_input(node.inputs[9])
|
||||
# sheen = c.parse_vector_input(node.inputs[10])
|
||||
# sheen_tint = c.parse_vector_input(node.inputs[11])
|
||||
# clearcoat = c.parse_vector_input(node.inputs[12])
|
||||
# clearcoat_rough = c.parse_vector_input(node.inputs[13])
|
||||
# ior = c.parse_vector_input(node.inputs[14])
|
||||
# transmission = c.parse_vector_input(node.inputs[15])
|
||||
# transmission_roughness = c.parse_vector_input(node.inputs[16])
|
||||
state.out_subsurface = c.parse_value_input(node.inputs[SUBSURFACE])
|
||||
state.out_subsurface_radius = c.parse_vector_input(node.inputs[SUBSURFACE_RADIUS])
|
||||
state.out_subsurface_color = c.parse_vector_input(node.inputs[SUBSURFACE_COLOR])
|
||||
state.out_metallic = c.parse_value_input(node.inputs['Metallic'])
|
||||
state.out_specular = c.parse_value_input(node.inputs[SPECULAR])
|
||||
state.out_specular_tint = 'vec3({})'.format(c.parse_value_input(node.inputs[SPECULAR_TINT]))
|
||||
state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
|
||||
state.out_anisotropy = c.parse_value_input(node.inputs[ANISOTROPIC])
|
||||
state.out_aniso_rot = c.parse_value_input(node.inputs[ANISOTROPIC_ROT])
|
||||
state.out_sheen = c.parse_value_input(node.inputs[SHEEN])
|
||||
state.out_sheen_tint = 'vec3({})'.format(c.parse_value_input(node.inputs[SHEEN_TINT]))
|
||||
state.out_clearcoat = c.parse_value_input(node.inputs[CLEARCOAT])
|
||||
state.out_clearcoat_rough = c.parse_value_input(node.inputs[CLEARCOAT_ROUGHNESS])
|
||||
state.out_transmission = c.parse_value_input(node.inputs[TRANSMISSION])
|
||||
state.out_transmission_rough = c.parse_value_input(node.inputs[TRANSMISSION_ROUGHNESS])
|
||||
state.out_tangent = c.parse_vector_input(node.inputs[TANGENT])
|
||||
if (node.inputs['Emission Strength'].is_linked or node.inputs['Emission Strength'].default_value != 0.0)\
|
||||
and (node.inputs['Emission'].is_linked or not mat_utils.equals_color_socket(node.inputs['Emission'], (0.0, 0.0, 0.0), comp_alpha=False)):
|
||||
emission_col = c.parse_vector_input(node.inputs[19])
|
||||
emission_strength = c.parse_value_input(node.inputs[20])
|
||||
and (node.inputs[EMISSION_COLOR].is_linked or not mat_utils.equals_color_socket(node.inputs[EMISSION_COLOR], (0.0, 0.0, 0.0), comp_alpha=False)):
|
||||
emission_col = c.parse_vector_input(node.inputs[EMISSION_COLOR])
|
||||
emission_strength = c.parse_value_input(node.inputs['Emission Strength'])
|
||||
state.out_emission_col = '({0} * {1})'.format(emission_col, emission_strength)
|
||||
mat_state.emission_type = mat_state.EmissionType.SHADED
|
||||
else:
|
||||
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
||||
# clearcoar_normal = c.parse_vector_input(node.inputs[21])
|
||||
# tangent = c.parse_vector_input(node.inputs[22])
|
||||
if state.parse_opacity:
|
||||
state.out_ior = c.parse_value_input(node.inputs[16])
|
||||
if len(node.inputs) >= 21:
|
||||
state.out_opacity = c.parse_value_input(node.inputs[21])
|
||||
if bpy.app.version > (4, 1, 0):
|
||||
state.out_ior = c.parse_value_input(node.inputs['IOR'])
|
||||
if 'Alpha' in node.inputs:
|
||||
state.out_opacity = c.parse_value_input(node.inputs['Alpha'])
|
||||
else:
|
||||
state.out_opacity = '1.0'
|
||||
if bpy.app.version >= (4, 0, 0):
|
||||
def parse_bsdfprincipled(node: bpy.types.ShaderNodeBsdfPrincipled, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
if state.parse_surface:
|
||||
c.write_normal(node.inputs[5])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
||||
c.write_normal(node.inputs['Normal'])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs['Base Color'])
|
||||
|
||||
sss_input = node.inputs.get('Subsurface Weight') or node.inputs.get('Subsurface')
|
||||
sss_input = node.inputs.get(SUBSURFACE)
|
||||
if sss_input is not None:
|
||||
if sss_input.is_linked or sss_input.default_value > 0.0:
|
||||
mat_state.needs_sss = True
|
||||
|
||||
subsurface = c.parse_value_input(node.inputs[7])
|
||||
subsurface_radius = c.parse_vector_input(node.inputs[9])
|
||||
subsurface_color = c.parse_vector_input(node.inputs[8])
|
||||
state.out_metallic = c.parse_value_input(node.inputs[1])
|
||||
state.out_subsurface = c.parse_value_input(sss_input)
|
||||
|
||||
specular_socket = node.inputs.get('Specular IOR Level')
|
||||
sss_radius_input = node.inputs.get(SUBSURFACE_RADIUS)
|
||||
if sss_radius_input is not None:
|
||||
state.out_subsurface_radius = c.parse_vector_input(sss_radius_input)
|
||||
|
||||
sss_scale_input = node.inputs.get(SUBSURFACE_SCALE)
|
||||
if sss_scale_input is not None:
|
||||
state.out_subsurface_scale = c.parse_value_input(sss_scale_input)
|
||||
|
||||
sss_aniso_input = node.inputs.get(SUBSURFACE_ANISOTROPY)
|
||||
if sss_aniso_input is not None:
|
||||
state.out_subsurface_anisotropy = c.parse_value_input(sss_aniso_input)
|
||||
|
||||
sss_color_input = node.inputs.get(SUBSURFACE_COLOR)
|
||||
if sss_color_input is not None:
|
||||
state.out_subsurface_color = c.parse_vector_input(sss_color_input)
|
||||
else:
|
||||
state.out_subsurface_color = c.parse_vector_input(node.inputs['Base Color'])
|
||||
|
||||
state.out_metallic = c.parse_value_input(node.inputs['Metallic'])
|
||||
|
||||
specular_socket = node.inputs.get(SPECULAR)
|
||||
if specular_socket is not None:
|
||||
state.out_specular = f'({c.parse_value_input(specular_socket)} * 2.0)'
|
||||
else:
|
||||
@ -172,89 +289,125 @@ if bpy.app.version > (4, 1, 0):
|
||||
state.out_specular = c.parse_value_input(specular_socket)
|
||||
else:
|
||||
state.out_specular = '1.0'
|
||||
|
||||
state.out_roughness = c.parse_value_input(node.inputs[2])
|
||||
|
||||
spec_tint_socket = node.inputs.get(SPECULAR_TINT)
|
||||
if spec_tint_socket is not None:
|
||||
state.out_specular_tint = c.parse_vector_input(spec_tint_socket)
|
||||
|
||||
state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
|
||||
# Prevent black material when metal = 1.0 and roughness = 0.0
|
||||
try:
|
||||
if float(state.out_roughness) < 0.00101:
|
||||
state.out_roughness = '0.001'
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
aniso_socket = node.inputs.get(ANISOTROPIC)
|
||||
if aniso_socket is not None:
|
||||
state.out_anisotropy = c.parse_value_input(aniso_socket)
|
||||
aniso_rot_socket = node.inputs.get(ANISOTROPIC_ROT)
|
||||
if aniso_rot_socket is not None:
|
||||
state.out_aniso_rot = c.parse_value_input(aniso_rot_socket)
|
||||
|
||||
sheen_socket = node.inputs.get(SHEEN)
|
||||
if sheen_socket is not None:
|
||||
state.out_sheen = c.parse_value_input(sheen_socket)
|
||||
sheen_rough_socket = node.inputs.get(SHEEN_ROUGHNESS)
|
||||
if sheen_rough_socket is not None:
|
||||
state.out_sheen_rough = c.parse_value_input(sheen_rough_socket)
|
||||
sheen_tint_socket = node.inputs.get(SHEEN_TINT)
|
||||
if sheen_tint_socket is not None:
|
||||
state.out_sheen_tint = c.parse_vector_input(sheen_tint_socket)
|
||||
|
||||
coat_socket = node.inputs.get(CLEARCOAT)
|
||||
if coat_socket is not None:
|
||||
state.out_clearcoat = c.parse_value_input(coat_socket)
|
||||
coat_rough_socket = node.inputs.get(CLEARCOAT_ROUGHNESS)
|
||||
if coat_rough_socket is not None:
|
||||
state.out_clearcoat_rough = c.parse_value_input(coat_rough_socket)
|
||||
coat_ior_socket = node.inputs.get(COAT_IOR)
|
||||
if coat_ior_socket is not None:
|
||||
state.out_coat_ior = c.parse_value_input(coat_ior_socket)
|
||||
coat_tint_socket = node.inputs.get(COAT_TINT)
|
||||
if coat_tint_socket is not None:
|
||||
state.out_coat_tint = c.parse_vector_input(coat_tint_socket)
|
||||
|
||||
trans_socket = node.inputs.get(TRANSMISSION)
|
||||
if trans_socket is not None:
|
||||
state.out_transmission = c.parse_value_input(trans_socket)
|
||||
trans_rough_socket = node.inputs.get(TRANSMISSION_ROUGHNESS)
|
||||
if trans_rough_socket is not None:
|
||||
state.out_transmission_rough = c.parse_value_input(trans_rough_socket)
|
||||
|
||||
thin_wall_socket = node.inputs.get(THIN_WALL)
|
||||
if thin_wall_socket is not None:
|
||||
state.out_thin_wall = c.parse_value_input(thin_wall_socket)
|
||||
|
||||
tangent_socket = node.inputs.get(TANGENT)
|
||||
if tangent_socket is not None:
|
||||
state.out_tangent = c.parse_vector_input(tangent_socket)
|
||||
|
||||
if (node.inputs['Emission Strength'].is_linked or node.inputs['Emission Strength'].default_value != 0.0)\
|
||||
and (node.inputs['Emission Color'].is_linked or not mat_utils.equals_color_socket(node.inputs['Emission Color'], (0.0, 0.0, 0.0), comp_alpha=False)):
|
||||
if bpy.app.version >= (4, 4, 0):
|
||||
emission_col = c.parse_vector_input(node.inputs[27])
|
||||
emission_strength = c.parse_value_input(node.inputs[28])
|
||||
else:
|
||||
emission_col = c.parse_vector_input(node.inputs[26])
|
||||
emission_strength = c.parse_value_input(node.inputs[27])
|
||||
and (node.inputs[EMISSION_COLOR].is_linked or not mat_utils.equals_color_socket(node.inputs[EMISSION_COLOR], (0.0, 0.0, 0.0), comp_alpha=False)):
|
||||
emission_col = c.parse_vector_input(node.inputs[EMISSION_COLOR])
|
||||
emission_strength = c.parse_value_input(node.inputs['Emission Strength'])
|
||||
state.out_emission_col = '({0} * {1})'.format(emission_col, emission_strength)
|
||||
mat_state.emission_type = mat_state.EmissionType.SHADED
|
||||
else:
|
||||
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
||||
#state.out_occlusion = state.out_roughness
|
||||
#state.out_aniso = c.parse_vector_input(node.inputs[14])
|
||||
#state.out_aniso_rot = c.parse_vector_input(node.inputs[15])
|
||||
#state.out_sheen = c.parse_vector_input(node.inputs[23])
|
||||
#state.out_sheen_tint = c.parse_vector_input(node.inputs[25])
|
||||
#state.out_clearcoat = c.parse_vector_input(node.inputs[18])
|
||||
#state.out_clearcoat_rough = c.parse_vector_input(node.inputs[19])
|
||||
#state.out_ior = c.parse_value_input(node.inputs[3])
|
||||
#state.out_transmission = c.parse_vector_input(node.inputs[17])
|
||||
#state.out_transmission_roughness = state.out_roughness
|
||||
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
|
||||
if state.parse_opacity:
|
||||
state.out_ior = c.parse_value_input(node.inputs[3])
|
||||
state.out_opacity = c.parse_value_input(node.inputs[4])
|
||||
state.out_ior = c.parse_value_input(node.inputs['IOR'])
|
||||
state.out_opacity = c.parse_value_input(node.inputs['Alpha'])
|
||||
|
||||
def parse_bsdfdiffuse(node: bpy.types.ShaderNodeBsdfDiffuse, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
if state.parse_surface:
|
||||
c.write_normal(node.inputs[2])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
||||
state.out_roughness = c.parse_value_input(node.inputs[1])
|
||||
c.write_normal(node.inputs['Normal'])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||
state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
|
||||
state.out_specular = '0.0'
|
||||
|
||||
if bpy.app.version >= (4, 0, 0):
|
||||
def parse_bsdfsheen(node: bpy.types.ShaderNodeBsdfSheen, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
if state.parse_surface:
|
||||
c.write_normal(node.inputs[2])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
||||
state.out_roughness = c.parse_value_input(node.inputs[1])
|
||||
c.write_normal(node.inputs['Normal'])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||
state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
|
||||
|
||||
if bpy.app.version < (4, 1, 0):
|
||||
if bpy.app.version < (4, 0, 0):
|
||||
def parse_bsdfglossy(node: bpy.types.ShaderNodeBsdfGlossy, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
if state.parse_surface:
|
||||
c.write_normal(node.inputs[2])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
||||
state.out_roughness = c.parse_value_input(node.inputs[1])
|
||||
c.write_normal(node.inputs['Normal'])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||
state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
|
||||
state.out_metallic = '1.0'
|
||||
else:
|
||||
def parse_bsdfglossy(node: bpy.types.ShaderNodeBsdfAnisotropic, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
if state.parse_surface:
|
||||
c.write_normal(node.inputs[4])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
||||
state.out_roughness = c.parse_value_input(node.inputs[1])
|
||||
c.write_normal(node.inputs['Normal'])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||
state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
|
||||
state.out_metallic = '1.0'
|
||||
|
||||
|
||||
def parse_ambientocclusion(node: bpy.types.ShaderNodeAmbientOcclusion, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
if state.parse_surface:
|
||||
# Single channel
|
||||
state.out_occlusion = c.parse_vector_input(node.inputs[0]) + '.r'
|
||||
state.out_occlusion = c.parse_vector_input(node.inputs['Distance']) + '.r'
|
||||
|
||||
|
||||
def parse_bsdfanisotropic(node: bpy.types.ShaderNodeBsdfAnisotropic, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
if state.parse_surface:
|
||||
c.write_normal(node.inputs[4])
|
||||
c.write_normal(node.inputs['Normal'])
|
||||
# Revert to glossy
|
||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
||||
state.out_roughness = c.parse_value_input(node.inputs[1])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||
state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
|
||||
state.out_metallic = '1.0'
|
||||
|
||||
|
||||
def parse_emission(node: bpy.types.ShaderNodeEmission, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
if state.parse_surface:
|
||||
emission_col = c.parse_vector_input(node.inputs[0])
|
||||
emission_strength = c.parse_value_input(node.inputs[1])
|
||||
emission_col = c.parse_vector_input(node.inputs['Color'])
|
||||
emission_strength = c.parse_value_input(node.inputs['Strength'])
|
||||
state.out_emission_col = '({0} * {1})'.format(emission_col, emission_strength)
|
||||
state.out_basecol = 'vec3(0.0)'
|
||||
state.out_specular = '0.0'
|
||||
@ -264,16 +417,77 @@ def parse_emission(node: bpy.types.ShaderNodeEmission, out_socket: NodeSocket, s
|
||||
|
||||
def parse_bsdfglass(node: bpy.types.ShaderNodeBsdfGlass, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
if state.parse_surface:
|
||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
||||
c.write_normal(node.inputs[3])
|
||||
state.out_roughness = c.parse_value_input(node.inputs[1])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||
c.write_normal(node.inputs['Normal'])
|
||||
state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
|
||||
if state.parse_opacity:
|
||||
state.out_opacity = '1.0'
|
||||
state.out_ior = c.parse_value_input(node.inputs[2])
|
||||
state.out_ior = c.parse_value_input(node.inputs['IOR'])
|
||||
|
||||
|
||||
def parse_bsdfhair(node: bpy.types.ShaderNodeBsdfHair, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
pass
|
||||
if state.parse_surface:
|
||||
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||
state.out_roughness = c.parse_value_input(node.inputs['RoughnessU'])
|
||||
state.out_specular = '0.0'
|
||||
rough_v_socket = node.inputs.get('RoughnessV')
|
||||
if rough_v_socket is not None:
|
||||
state.out_aniso_rot = c.parse_value_input(rough_v_socket)
|
||||
tangent_socket = node.inputs.get('Tangent')
|
||||
if tangent_socket is not None:
|
||||
state.out_tangent = c.parse_vector_input(tangent_socket)
|
||||
|
||||
|
||||
if bpy.app.version >= (2, 83, 0):
|
||||
def parse_bsdfhairprincipled(node: 'bpy.types.ShaderNodeBsdfHairPrincipled', out_socket: NodeSocket, state: ParserState) -> None:
|
||||
if state.parse_surface:
|
||||
c.write_normal(node.inputs['Normal'])
|
||||
parametrization = getattr(node, 'parametrization', 'COLOR')
|
||||
if parametrization == 'COLOR':
|
||||
color_socket = node.inputs.get('Color')
|
||||
if color_socket is not None:
|
||||
state.out_basecol = c.parse_vector_input(color_socket)
|
||||
elif parametrization == 'MELANIN':
|
||||
melanin_socket = node.inputs.get('Melanin')
|
||||
if melanin_socket is not None:
|
||||
state.out_subsurface = c.parse_value_input(melanin_socket)
|
||||
melanin_red_socket = node.inputs.get('Melanin Redness')
|
||||
if melanin_red_socket is not None:
|
||||
state.out_specular_tint = 'vec3({})'.format(c.parse_value_input(melanin_red_socket))
|
||||
elif parametrization == 'ABSORPTION':
|
||||
absorption_socket = node.inputs.get('Absorption Coefficient')
|
||||
if absorption_socket is not None:
|
||||
state.out_specular_tint = c.parse_vector_input(absorption_socket)
|
||||
tint_socket = node.inputs.get('Tint')
|
||||
if tint_socket is not None:
|
||||
state.out_subsurface_color = c.parse_vector_input(tint_socket)
|
||||
rough_socket = node.inputs.get('Roughness')
|
||||
if rough_socket is not None:
|
||||
state.out_roughness = c.parse_value_input(rough_socket)
|
||||
model = getattr(node, 'model', 'CHIANG')
|
||||
if model == 'CHIANG':
|
||||
radial_rough_socket = node.inputs.get('Radial Roughness')
|
||||
if radial_rough_socket is not None:
|
||||
state.out_aniso_rot = c.parse_value_input(radial_rough_socket)
|
||||
coat_socket = node.inputs.get('Coat')
|
||||
if coat_socket is not None:
|
||||
state.out_clearcoat = c.parse_value_input(coat_socket)
|
||||
random_color_socket = node.inputs.get('Random Color')
|
||||
if random_color_socket is not None:
|
||||
state.out_sheen = c.parse_value_input(random_color_socket)
|
||||
random_rough_socket = node.inputs.get('Random Roughness')
|
||||
if random_rough_socket is not None:
|
||||
state.out_sheen_rough = c.parse_value_input(random_rough_socket)
|
||||
offset_socket = node.inputs.get('Offset')
|
||||
if offset_socket is not None:
|
||||
state.out_anisotropy = c.parse_value_input(offset_socket)
|
||||
state.out_specular = '0.0'
|
||||
state.out_metallic = '0.0'
|
||||
if state.parse_opacity:
|
||||
ior_socket = node.inputs.get('IOR')
|
||||
if ior_socket is not None:
|
||||
state.out_ior = c.parse_value_input(ior_socket)
|
||||
state.out_opacity = '1.0'
|
||||
|
||||
|
||||
def parse_holdout(node: bpy.types.ShaderNodeHoldout, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
@ -284,46 +498,50 @@ def parse_holdout(node: bpy.types.ShaderNodeHoldout, out_socket: NodeSocket, sta
|
||||
|
||||
def parse_bsdfrefraction(node: bpy.types.ShaderNodeBsdfRefraction, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
if state.parse_surface:
|
||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
||||
c.write_normal(node.inputs[3])
|
||||
state.out_roughness = c.parse_value_input(node.inputs[1])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||
c.write_normal(node.inputs['Normal'])
|
||||
state.out_roughness = c.parse_value_input(node.inputs['Roughness'])
|
||||
if state.parse_opacity:
|
||||
state.out_opacity = '1.0'
|
||||
state.out_ior = c.parse_value_input(node.inputs[2])
|
||||
state.out_ior = c.parse_value_input(node.inputs['IOR'])
|
||||
|
||||
def parse_subsurfacescattering(node: bpy.types.ShaderNodeSubsurfaceScattering, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
if state.parse_surface:
|
||||
# Mark that this material needs SSS
|
||||
mat_state.needs_sss = True
|
||||
if bpy.app.version < (4, 1, 0):
|
||||
c.write_normal(node.inputs[4])
|
||||
else:
|
||||
c.write_normal(node.inputs[6])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
||||
c.write_normal(node.inputs['Normal'])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||
state.out_subsurface = c.parse_value_input(node.inputs['Scale'])
|
||||
state.out_subsurface_radius = c.parse_vector_input(node.inputs['Radius'])
|
||||
state.out_subsurface_color = c.parse_vector_input(node.inputs['Color'])
|
||||
state.out_specular = '0.0'
|
||||
|
||||
|
||||
def parse_bsdftoon(node: bpy.types.ShaderNodeBsdfToon, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
# c.write_normal(node.inputs[3])
|
||||
pass
|
||||
if state.parse_surface:
|
||||
c.write_normal(node.inputs['Normal'])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||
state.out_roughness = c.parse_value_input(node.inputs['Size'])
|
||||
state.out_specular = '0.0'
|
||||
|
||||
|
||||
def parse_bsdftranslucent(node: bpy.types.ShaderNodeBsdfTranslucent, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
if state.parse_surface:
|
||||
c.write_normal(node.inputs[1])
|
||||
c.write_normal(node.inputs['Normal'])
|
||||
if state.parse_opacity:
|
||||
state.out_opacity = '(1.0 - {0}.r)'.format(c.parse_vector_input(node.inputs[0]))
|
||||
state.out_opacity = '(1.0 - {0}.r)'.format(c.parse_vector_input(node.inputs['Color']))
|
||||
state.out_ior = '1.0'
|
||||
|
||||
|
||||
def parse_bsdftransparent(node: bpy.types.ShaderNodeBsdfTransparent, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
if state.parse_opacity:
|
||||
state.out_opacity = '(1.0 - {0}.r)'.format(c.parse_vector_input(node.inputs[0]))
|
||||
state.out_opacity = '(1.0 - {0}.r)'.format(c.parse_vector_input(node.inputs['Color']))
|
||||
state.out_ior = '1.0'
|
||||
|
||||
if bpy.app.version < (4, 1, 0):
|
||||
if bpy.app.version < (4, 0, 0):
|
||||
def parse_bsdfvelvet(node: bpy.types.ShaderNodeBsdfVelvet, out_socket: NodeSocket, state: ParserState) -> None:
|
||||
if state.parse_surface:
|
||||
c.write_normal(node.inputs[2])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs[0])
|
||||
c.write_normal(node.inputs['Normal'])
|
||||
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
|
||||
state.out_roughness = '1.0'
|
||||
state.out_metallic = '1.0'
|
||||
|
||||
@ -40,6 +40,8 @@ def parse(material: Material, mat_data, mat_users: Dict[Material, List[Object]],
|
||||
wrd = bpy.data.worlds['Lnx']
|
||||
rpdat = lnx.utils.get_rp()
|
||||
|
||||
mat_state.features = {}
|
||||
|
||||
# Texture caching for material batching
|
||||
batch_cached_textures = []
|
||||
|
||||
@ -120,6 +122,76 @@ def parse(material: Material, mat_data, mat_users: Dict[Material, List[Object]],
|
||||
const['intValue'] = 0
|
||||
c['bind_constants'].append(const)
|
||||
|
||||
# extended BRDF parameters as bind_constants
|
||||
if rpdat.rp_renderer == 'Deferred':
|
||||
feats = mat_state.features
|
||||
|
||||
def try_float(val_str, default=1.0):
|
||||
try:
|
||||
return float(val_str)
|
||||
except (ValueError, TypeError):
|
||||
return default
|
||||
|
||||
has_ext_brdf = False
|
||||
if feats.get('clearcoat', '0.0') not in ('0.0', '0', ''):
|
||||
c['bind_constants'].append({'name': 'clearcoat', 'floatValue': try_float(feats.get('clearcoat', '1.0'))})
|
||||
c['bind_constants'].append({'name': 'clearcoatRough', 'floatValue': try_float(feats.get('clearcoatRough', '0.03'), 0.03)})
|
||||
c['bind_constants'].append({'name': 'coatIOR', 'floatValue': try_float(feats.get('coatIOR', '1.5'), 1.5)})
|
||||
c['bind_constants'].append({'name': 'coatTintR', 'floatValue': try_float(feats.get('coatTintR', '1.0'), 1.0)})
|
||||
c['bind_constants'].append({'name': 'coatTintG', 'floatValue': try_float(feats.get('coatTintG', '1.0'), 1.0)})
|
||||
c['bind_constants'].append({'name': 'coatTintB', 'floatValue': try_float(feats.get('coatTintB', '1.0'), 1.0)})
|
||||
if '_ClearCoat' not in wrd.world_defs:
|
||||
wrd.world_defs += '_ClearCoat'
|
||||
has_ext_brdf = True
|
||||
if feats.get('sheen', '0.0') not in ('0.0', '0', ''):
|
||||
c['bind_constants'].append({'name': 'sheen', 'floatValue': try_float(feats.get('sheen', '1.0'))})
|
||||
c['bind_constants'].append({'name': 'sheenRough', 'floatValue': try_float(feats.get('sheenRough', '0.5'), 0.5)})
|
||||
c['bind_constants'].append({'name': 'sheenTintR', 'floatValue': try_float(feats.get('sheenTintR', '1.0'), 1.0)})
|
||||
c['bind_constants'].append({'name': 'sheenTintG', 'floatValue': try_float(feats.get('sheenTintG', '1.0'), 1.0)})
|
||||
c['bind_constants'].append({'name': 'sheenTintB', 'floatValue': try_float(feats.get('sheenTintB', '1.0'), 1.0)})
|
||||
if '_Sheen' not in wrd.world_defs:
|
||||
wrd.world_defs += '_Sheen'
|
||||
has_ext_brdf = True
|
||||
if feats.get('subsurface', '0.0') not in ('0.0', '0', '') and rpdat.rp_sss_state != 'Off' and not mat_uses_sss:
|
||||
c['bind_constants'].append({'name': 'subsurface', 'floatValue': try_float(feats.get('subsurface', '1.0'))})
|
||||
c['bind_constants'].append({'name': 'subsurfaceAnisotropy', 'floatValue': try_float(feats.get('subsurfaceAnisotropy', '0.0'), 0.0)})
|
||||
c['bind_constants'].append({'name': 'subsurfaceRadiusR', 'floatValue': try_float(feats.get('subsurfaceRadiusR', '1.0'), 1.0)})
|
||||
c['bind_constants'].append({'name': 'subsurfaceRadiusG', 'floatValue': try_float(feats.get('subsurfaceRadiusG', '0.2'), 0.2)})
|
||||
c['bind_constants'].append({'name': 'subsurfaceRadiusB', 'floatValue': try_float(feats.get('subsurfaceRadiusB', '0.1'), 0.1)})
|
||||
c['bind_constants'].append({'name': 'subsurfaceColorR', 'floatValue': try_float(feats.get('subsurfaceColorR', '0.8'), 0.8)})
|
||||
c['bind_constants'].append({'name': 'subsurfaceColorG', 'floatValue': try_float(feats.get('subsurfaceColorG', '0.8'), 0.8)})
|
||||
c['bind_constants'].append({'name': 'subsurfaceColorB', 'floatValue': try_float(feats.get('subsurfaceColorB', '0.8'), 0.8)})
|
||||
if '_Subsurface' not in wrd.world_defs:
|
||||
wrd.world_defs += '_Subsurface'
|
||||
has_ext_brdf = True
|
||||
if feats.get('anisotropy', '0.0') not in ('0.0', '0', ''):
|
||||
c['bind_constants'].append({'name': 'anisotropy', 'floatValue': try_float(feats.get('anisotropy', '1.0'))})
|
||||
c['bind_constants'].append({'name': 'anisoRot', 'floatValue': try_float(feats.get('anisoRot', '0.0'), 0.0)})
|
||||
if '_Anisotropy' not in wrd.world_defs:
|
||||
wrd.world_defs += '_Anisotropy'
|
||||
has_ext_brdf = True
|
||||
if feats.get('transmission', '0.0') not in ('0.0', '0', ''):
|
||||
c['bind_constants'].append({'name': 'transmission', 'floatValue': try_float(feats.get('transmission', '1.0'))})
|
||||
c['bind_constants'].append({'name': 'transmissionRough', 'floatValue': try_float(feats.get('transmissionRough', '0.0'), 0.0)})
|
||||
c['bind_constants'].append({'name': 'ior', 'floatValue': try_float(feats.get('ior', '1.45'), 1.45)})
|
||||
c['bind_constants'].append({'name': 'thinWall', 'floatValue': try_float(feats.get('thinWall', '0.0'), 0.0)})
|
||||
if '_Transmission' not in wrd.world_defs:
|
||||
wrd.world_defs += '_Transmission'
|
||||
has_ext_brdf = True
|
||||
|
||||
if has_ext_brdf and '_ExtBRDF' not in wrd.world_defs:
|
||||
wrd.world_defs += '_ExtBRDF'
|
||||
|
||||
# assign materialID for extended BRDF if not already set
|
||||
# coexist with Scene.hx for last materialID in bind_constants
|
||||
if has_ext_brdf and material.lnx_material_id == 0:
|
||||
if mat_state.next_ext_mat_id <= 15:
|
||||
ext_id = mat_state.next_ext_mat_id
|
||||
mat_state.next_ext_mat_id += 1
|
||||
c['bind_constants'].append({'name': 'materialID', 'intValue': ext_id})
|
||||
else:
|
||||
log.warn(f'material "{material.name}": extended BRDF material limit (15) exceeded, extended BRDF will be disabled for this material')
|
||||
|
||||
# TODO: Mesh only material batching
|
||||
if wrd.lnx_batch_materials:
|
||||
# Set textures uniforms
|
||||
|
||||
@ -111,6 +111,16 @@ def write(vert: shader.Shader, frag: shader.Shader):
|
||||
frag.add_uniform('mat4 invVP', '_inverseViewProjectionMatrix')
|
||||
frag.add_uniform('vec3 eye', '_cameraPosition')
|
||||
frag.write(', gbufferD, invVP, eye')
|
||||
if '_ClearCoat' in wrd.world_defs:
|
||||
frag.write(', clearcoat, clearcoatRough, coatIOR, coatTint')
|
||||
if '_Sheen' in wrd.world_defs:
|
||||
frag.write(', sheen, sheenRough, sheenTint')
|
||||
if '_Anisotropy' in wrd.world_defs:
|
||||
frag.write(', anisotropy, anisoRot, tangent')
|
||||
if '_Subsurface' in wrd.world_defs:
|
||||
frag.write(', subsurface, subsurfaceColor, subsurfaceRadius, subsurfaceAnisotropy')
|
||||
if '_Transmission' in wrd.world_defs:
|
||||
frag.write(', transmission, transmissionRough, ior, thinWall')
|
||||
frag.write(');')
|
||||
|
||||
frag.write('}') # for numLights
|
||||
|
||||
@ -74,6 +74,8 @@ def make(context_id, rpasses):
|
||||
con['color_attachments'] = [attachment_format, attachment_format]
|
||||
if '_gbuffer2' in wrd.world_defs:
|
||||
con['color_attachments'].append(attachment_format)
|
||||
if '_Anisotropy' in wrd.world_defs:
|
||||
con['color_attachments'].append('RGBA64')
|
||||
|
||||
con_mesh = mat_state.data.add_context(con)
|
||||
mat_state.con_mesh = con_mesh
|
||||
@ -252,7 +254,9 @@ def make_deferred(con_mesh, rpasses):
|
||||
frag.write('n.xy = n.z >= 0.0 ? n.xy : octahedronWrap(n.xy);')
|
||||
|
||||
is_shadeless = mat_state.emission_type == mat_state.EmissionType.SHADELESS
|
||||
if is_shadeless or '_SSS' in wrd.world_defs or '_Hair' in wrd.world_defs:
|
||||
ext_brdf_defs = ('_ClearCoat', '_Sheen', '_Anisotropy', '_Subsurface', '_Transmission')
|
||||
has_ext_brdf = any(d in wrd.world_defs for d in ext_brdf_defs)
|
||||
if is_shadeless or '_SSS' in wrd.world_defs or '_Hair' in wrd.world_defs or has_ext_brdf:
|
||||
frag.write('uint matid = 0;')
|
||||
if is_shadeless:
|
||||
frag.write('matid = 1;')
|
||||
@ -260,6 +264,9 @@ def make_deferred(con_mesh, rpasses):
|
||||
if '_SSS' in wrd.world_defs or '_Hair' in wrd.world_defs:
|
||||
frag.add_uniform('int materialID')
|
||||
frag.write('if (materialID == 2) matid = 2;')
|
||||
if has_ext_brdf:
|
||||
frag.add_uniform('int materialID')
|
||||
frag.write('if (materialID >= 3) matid = uint(materialID);')
|
||||
else:
|
||||
frag.write('const uint matid = 0;')
|
||||
|
||||
@ -285,6 +292,14 @@ def make_deferred(con_mesh, rpasses):
|
||||
if '_SSRefraction' in wrd.world_defs or '_VoxelRefract' in wrd.world_defs:
|
||||
frag.write('fragColor[GBUF_IDX_REFRACTION] = vec4(1.0, 0.0, 0.0, 1.0);')
|
||||
|
||||
if '_Anisotropy' in wrd.world_defs:
|
||||
frag.write('#ifdef _Anisotropy')
|
||||
if con_mesh.is_elem('tang'):
|
||||
frag.write('fragColor[GBUF_IDX_3] = vec4(normalize(TBN[0]), 0.0);')
|
||||
else:
|
||||
frag.write('fragColor[GBUF_IDX_3] = vec4(0.0, 0.0, 0.0, 0.0);')
|
||||
frag.write('#endif')
|
||||
|
||||
return con_mesh
|
||||
|
||||
|
||||
@ -830,6 +845,16 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
|
||||
frag.add_uniform('mat4 invVP', '_inverseViewProjectionMatrix')
|
||||
frag.add_uniform('vec3 eye', '_cameraPosition')
|
||||
frag.write(', gbufferD, invVP, eye')
|
||||
if '_ClearCoat' in wrd.world_defs:
|
||||
frag.write(', clearcoat, clearcoatRough, coatIOR, coatTint')
|
||||
if '_Sheen' in wrd.world_defs:
|
||||
frag.write(', sheen, sheenRough, sheenTint')
|
||||
if '_Anisotropy' in wrd.world_defs:
|
||||
frag.write(', anisotropy, anisoRot, tangent')
|
||||
if '_Subsurface' in wrd.world_defs:
|
||||
frag.write(', subsurface, subsurfaceColor, subsurfaceRadius, subsurfaceAnisotropy')
|
||||
if '_Transmission' in wrd.world_defs:
|
||||
frag.write(', transmission, transmissionRough, ior, thinWall')
|
||||
frag.write(');')
|
||||
|
||||
if '_Clusters' in wrd.world_defs:
|
||||
@ -856,6 +881,26 @@ def _write_material_attribs_default(frag: shader.Shader, parse_opacity: bool):
|
||||
# We may not use emission, but the attribute will then be removed
|
||||
# by the shader compiler
|
||||
frag.write('vec3 emissionCol;')
|
||||
# Extended BRDF parameters
|
||||
frag.write('float subsurface = 0.0;')
|
||||
frag.write('vec3 subsurfaceRadius = vec3(0.0);')
|
||||
frag.write('vec3 subsurfaceColor = vec3(0.8);')
|
||||
frag.write('vec3 specularTint = vec3(1.0);')
|
||||
frag.write('float anisotropy = 0.0;')
|
||||
frag.write('float anisoRot = 0.0;')
|
||||
frag.write('float sheen = 0.0;')
|
||||
frag.write('float sheenRough = 0.5;')
|
||||
frag.write('vec3 sheenTint = vec3(1.0);')
|
||||
frag.write('float clearcoat = 0.0;')
|
||||
frag.write('float clearcoatRough = 0.03;')
|
||||
frag.write('float transmission = 0.0;')
|
||||
frag.write('float transmissionRough = 0.0;')
|
||||
frag.write('float thinWall = 0.0;')
|
||||
frag.write('vec3 tangent = vec3(0.0);')
|
||||
frag.write('float subsurfaceScale = 0.05;')
|
||||
frag.write('float subsurfaceAnisotropy = 0.0;')
|
||||
frag.write('float coatIOR = 1.5;')
|
||||
frag.write('vec3 coatTint = vec3(1.0);')
|
||||
if parse_opacity:
|
||||
frag.write('float opacity;')
|
||||
frag.write('float ior = 1.45;')
|
||||
|
||||
@ -81,6 +81,25 @@ def make_gi(context_id):
|
||||
frag.write('float occlusion;') #
|
||||
frag.write('float specular;') #
|
||||
frag.write('vec3 emissionCol = vec3(0.0);')
|
||||
frag.write('vec3 specularTint = vec3(1.0);')
|
||||
frag.write('float subsurface = 0.0;')
|
||||
frag.write('vec3 subsurfaceRadius = vec3(0.0);')
|
||||
frag.write('vec3 subsurfaceColor = vec3(0.8);')
|
||||
frag.write('float anisotropy = 0.0;')
|
||||
frag.write('float anisoRot = 0.0;')
|
||||
frag.write('float sheen = 0.0;')
|
||||
frag.write('float sheenRough = 0.5;')
|
||||
frag.write('vec3 sheenTint = vec3(1.0);')
|
||||
frag.write('float clearcoat = 0.0;')
|
||||
frag.write('float clearcoatRough = 0.03;')
|
||||
frag.write('float transmission = 0.0;')
|
||||
frag.write('float transmissionRough = 0.0;')
|
||||
frag.write('float thinWall = 0.0;')
|
||||
frag.write('vec3 tangent = vec3(0.0);')
|
||||
frag.write('float subsurfaceScale = 0.05;')
|
||||
frag.write('float subsurfaceAnisotropy = 0.0;')
|
||||
frag.write('float coatIOR = 1.5;')
|
||||
frag.write('vec3 coatTint = vec3(1.0);')
|
||||
blend = mat_state.material.lnx_blending
|
||||
parse_opacity = blend or mat_utils.is_transluc(mat_state.material)
|
||||
if parse_opacity:
|
||||
@ -377,6 +396,16 @@ def make_gi(context_id):
|
||||
frag.write(', opacity != 1.0')
|
||||
if '_Spot' in wrd.world_defs:
|
||||
frag.write(', true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight')
|
||||
if '_ClearCoat' in wrd.world_defs:
|
||||
frag.write(', clearcoat, clearcoatRough, coatIOR, coatTint')
|
||||
if '_Sheen' in wrd.world_defs:
|
||||
frag.write(', sheen, sheenRough, sheenTint')
|
||||
if '_Anisotropy' in wrd.world_defs:
|
||||
frag.write(', anisotropy, anisoRot, tangent')
|
||||
if '_Subsurface' in wrd.world_defs:
|
||||
frag.write(', subsurface, subsurfaceColor, subsurfaceRadius, subsurfaceAnisotropy')
|
||||
if '_Transmission' in wrd.world_defs:
|
||||
frag.write(', transmission, transmissionRough, ior, thinWall')
|
||||
frag.write(');')
|
||||
|
||||
if '_Clusters' in wrd.world_defs:
|
||||
@ -458,6 +487,16 @@ def make_gi(context_id):
|
||||
frag.write('\t, lightsArraySpot[li * 2].xyz') # spotDir
|
||||
frag.write('\t, vec2(lightsArray[li * 3].w, lightsArray[li * 3 + 1].w)') # scale
|
||||
frag.write('\t, lightsArraySpot[li * 2 + 1].xyz') # right
|
||||
if '_ClearCoat' in wrd.world_defs:
|
||||
frag.write('\t, clearcoat, clearcoatRough, coatIOR, coatTint')
|
||||
if '_Sheen' in wrd.world_defs:
|
||||
frag.write('\t, sheen, sheenRough, sheenTint')
|
||||
if '_Anisotropy' in wrd.world_defs:
|
||||
frag.write('\t, anisotropy, anisoRot, tangent')
|
||||
if '_Subsurface' in wrd.world_defs:
|
||||
frag.write('\t, subsurface, subsurfaceColor, subsurfaceRadius, subsurfaceAnisotropy')
|
||||
if '_Transmission' in wrd.world_defs:
|
||||
frag.write('\t, transmission, transmissionRough, ior, thinWall')
|
||||
frag.write(' );')
|
||||
frag.write('}')
|
||||
|
||||
|
||||
@ -39,3 +39,5 @@ con_mesh = None # Mesh context
|
||||
uses_instancing = False # Whether the current material has at least one user with instancing enabled
|
||||
emission_type = EmissionType.NO_EMISSION
|
||||
needs_sss = False
|
||||
features = {} # tracks extended BRDF features used by current material
|
||||
next_ext_mat_id = 3 # auto assigned materialID for extended BRDF
|
||||
|
||||
@ -165,6 +165,10 @@ ALL_NODES: Dict[str, MaterialNodeMeta] = {
|
||||
'TRANSPARENT_BSDF': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdftransparent),
|
||||
'BSDF_REFRACTION': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfrefraction),
|
||||
'REFRACTION_BSDF': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfrefraction),
|
||||
'BSDF_TOON': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdftoon),
|
||||
'TOON_BSDF': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdftoon),
|
||||
'BSDF_HAIR': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfhair),
|
||||
'HAIR_BSDF': MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfhair),
|
||||
'EMISSION': MaterialNodeMeta(parse_func=nodes_shader.parse_emission),
|
||||
'HOLDOUT': MaterialNodeMeta(
|
||||
parse_func=nodes_shader.parse_holdout,
|
||||
@ -205,11 +209,14 @@ ALL_NODES: Dict[str, MaterialNodeMeta] = {
|
||||
if bpy.app.version > (3, 2, 0):
|
||||
ALL_NODES['SEPARATE_COLOR'] = MaterialNodeMeta(parse_func=nodes_converter.parse_separate_color)
|
||||
ALL_NODES['COMBINE_COLOR'] = MaterialNodeMeta(parse_func=nodes_converter.parse_combine_color)
|
||||
if bpy.app.version < (4, 1, 0):
|
||||
if bpy.app.version < (4, 0, 0):
|
||||
ALL_NODES['BSDF_VELVET'] = MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfvelvet)
|
||||
if bpy.app.version < (4, 1, 0):
|
||||
ALL_NODES['TEX_MUSGRAVE'] = MaterialNodeMeta(parse_func=nodes_texture.parse_tex_musgrave)
|
||||
if bpy.app.version >= (4, 0, 0):
|
||||
ALL_NODES['BSDF_SHEEN'] = MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfsheen)
|
||||
if bpy.app.version >= (2, 83, 0):
|
||||
ALL_NODES['BSDF_HAIR_PRINCIPLED'] = MaterialNodeMeta(parse_func=nodes_shader.parse_bsdfhairprincipled)
|
||||
if bpy.app.version < (5, 0, 0):
|
||||
ALL_NODES['TEX_POINTDENSITY'] = MaterialNodeMeta(parse_func=nodes_texture.parse_tex_pointdensity, compute_dxdy_variants=ComputeDXDYVariant.NEVER)
|
||||
|
||||
|
||||
@ -99,6 +99,25 @@ class ParserState:
|
||||
self.out_opacity: floatstr = '1.0'
|
||||
self.out_ior: floatstr = '1.450'
|
||||
self.out_emission_col: vec3str = 'vec3(0.0)'
|
||||
self.out_subsurface: floatstr = '0.0'
|
||||
self.out_subsurface_radius: vec3str = 'vec3(0.0)'
|
||||
self.out_subsurface_color: vec3str = 'vec3(0.8)'
|
||||
self.out_specular_tint: vec3str = 'vec3(1.0)'
|
||||
self.out_anisotropy: floatstr = '0.0'
|
||||
self.out_aniso_rot: floatstr = '0.0'
|
||||
self.out_sheen: floatstr = '0.0'
|
||||
self.out_sheen_rough: floatstr = '0.5'
|
||||
self.out_sheen_tint: vec3str = 'vec3(1.0)'
|
||||
self.out_clearcoat: floatstr = '0.0'
|
||||
self.out_clearcoat_rough: floatstr = '0.03'
|
||||
self.out_transmission: floatstr = '0.0'
|
||||
self.out_transmission_rough: floatstr = '0.0'
|
||||
self.out_thin_wall: floatstr = '0.0'
|
||||
self.out_tangent: vec3str = 'vec3(0.0)'
|
||||
self.out_subsurface_scale: floatstr = '0.05'
|
||||
self.out_subsurface_anisotropy: floatstr = '0.0'
|
||||
self.out_coat_ior: floatstr = '1.5'
|
||||
self.out_coat_tint: vec3str = 'vec3(1.0)'
|
||||
|
||||
def reset_outs(self):
|
||||
"""Reset the shader output values to their default values."""
|
||||
@ -110,11 +129,40 @@ class ParserState:
|
||||
self.out_opacity = '1.0'
|
||||
self.out_ior = '1.450'
|
||||
self.out_emission_col = 'vec3(0.0)'
|
||||
self.out_subsurface = '0.0'
|
||||
self.out_subsurface_radius = 'vec3(0.0)'
|
||||
self.out_subsurface_color = 'vec3(0.8)'
|
||||
self.out_specular_tint = 'vec3(1.0)'
|
||||
self.out_anisotropy = '0.0'
|
||||
self.out_aniso_rot = '0.0'
|
||||
self.out_sheen = '0.0'
|
||||
self.out_sheen_rough = '0.5'
|
||||
self.out_sheen_tint = 'vec3(1.0)'
|
||||
self.out_clearcoat = '0.0'
|
||||
self.out_clearcoat_rough = '0.03'
|
||||
self.out_transmission = '0.0'
|
||||
self.out_transmission_rough = '0.0'
|
||||
self.out_thin_wall = '0.0'
|
||||
self.out_tangent = 'vec3(0.0)'
|
||||
self.out_subsurface_scale = '0.05'
|
||||
self.out_subsurface_anisotropy = '0.0'
|
||||
self.out_coat_ior = '1.5'
|
||||
self.out_coat_tint = 'vec3(1.0)'
|
||||
|
||||
def get_outs(self) -> Tuple[vec3str, floatstr, floatstr, floatstr, floatstr, floatstr, floatstr, vec3str]:
|
||||
def get_outs(self) -> Tuple:
|
||||
"""Return the shader output values as a tuple."""
|
||||
return (self.out_basecol, self.out_roughness, self.out_metallic, self.out_occlusion, self.out_specular,
|
||||
self.out_opacity, self.out_ior, self.out_emission_col)
|
||||
return (self.out_basecol, self.out_roughness, self.out_metallic,
|
||||
self.out_occlusion, self.out_specular, self.out_opacity,
|
||||
self.out_ior, self.out_emission_col,
|
||||
self.out_subsurface, self.out_subsurface_radius,
|
||||
self.out_subsurface_color, self.out_specular_tint,
|
||||
self.out_anisotropy, self.out_aniso_rot,
|
||||
self.out_sheen, self.out_sheen_rough, self.out_sheen_tint,
|
||||
self.out_clearcoat, self.out_clearcoat_rough,
|
||||
self.out_transmission, self.out_transmission_rough,
|
||||
self.out_thin_wall, self.out_tangent,
|
||||
self.out_subsurface_scale, self.out_subsurface_anisotropy,
|
||||
self.out_coat_ior, self.out_coat_tint)
|
||||
|
||||
|
||||
def get_parser_pass_suffix(self) -> str:
|
||||
|
||||
@ -11,12 +11,16 @@ import sys
|
||||
import os
|
||||
import array
|
||||
import atexit
|
||||
import time
|
||||
from mathutils import Quaternion, Matrix, Vector
|
||||
import lnx.make_state as state
|
||||
|
||||
HAS_GPU_STATE = bpy.app.version >= (3, 0, 0)
|
||||
if not HAS_GPU_STATE:
|
||||
try:
|
||||
import bgl
|
||||
HAS_BGL = callable(getattr(bgl, 'glGenTextures', None))
|
||||
except ImportError:
|
||||
HAS_BGL = False
|
||||
SHADER_UNIFORM_COLOR = 'UNIFORM_COLOR' if bpy.app.version >= (3, 4, 0) else '2D_UNIFORM_COLOR'
|
||||
SHADER_IMAGE = 'IMAGE' if bpy.app.version >= (3, 4, 0) else '2D_IMAGE'
|
||||
HAS_GPU_TEXTURE = bpy.app.version >= (3, 0, 0)
|
||||
@ -124,6 +128,8 @@ class KromViewportEngine(bpy.types.RenderEngine):
|
||||
self._last_frame_id = 0
|
||||
self._width = 0
|
||||
self._height = 0
|
||||
self._tex_width = 0
|
||||
self._tex_height = 0
|
||||
self._initialized = False
|
||||
self._shader = None
|
||||
self._batch = None
|
||||
@ -140,6 +146,8 @@ class KromViewportEngine(bpy.types.RenderEngine):
|
||||
self._last_region_dims = None
|
||||
self._header_buffer = (ctypes.c_ubyte * VIEWPORT_HEADER_SIZE)()
|
||||
self._engine_id = id(self)
|
||||
self._viewport_image = None
|
||||
self._float_buffer = None
|
||||
# print(f"New engine instance created: {self._engine_id}")
|
||||
|
||||
def _restore_overlay(self, context=None):
|
||||
@ -247,7 +255,7 @@ class KromViewportEngine(bpy.types.RenderEngine):
|
||||
def deferred_launch():
|
||||
try:
|
||||
import lnx.make as make
|
||||
make.play_viewport(viewport_id, width, height)
|
||||
make.play(viewport_id, width, height)
|
||||
except Exception as e:
|
||||
print(f"Failed to launch viewport: {e}")
|
||||
import traceback
|
||||
@ -498,7 +506,7 @@ class KromViewportEngine(bpy.types.RenderEngine):
|
||||
"""Handle invalid/stale shared memory by cleaning up and allowing re-init."""
|
||||
self._cleanup_shared_memory()
|
||||
self._initialized = False
|
||||
if not HAS_GPU_TEXTURE and hasattr(self, '_gl_texture_buf') and self._gl_texture_buf[0] != 0:
|
||||
if HAS_BGL and hasattr(self, '_gl_texture_buf') and self._gl_texture_buf[0] != 0:
|
||||
try:
|
||||
bgl.glDeleteTextures(1, self._gl_texture_buf)
|
||||
except:
|
||||
@ -783,21 +791,8 @@ class KromViewportEngine(bpy.types.RenderEngine):
|
||||
if len(pixels) < num_pixels:
|
||||
return
|
||||
|
||||
if HAS_GPU_TEXTURE:
|
||||
if HAS_NUMPY:
|
||||
pixels[3::4] = 255
|
||||
float_array = pixels.astype(np.float32) / 255.0
|
||||
buffer = gpu.types.Buffer('FLOAT', num_pixels, float_array)
|
||||
else:
|
||||
pixel_array = array.array('B', pixels[:num_pixels])
|
||||
for i in range(3, len(pixel_array), 4):
|
||||
pixel_array[i] = 255
|
||||
float_pixels = [p / 255.0 for p in pixel_array]
|
||||
buffer = gpu.types.Buffer('FLOAT', num_pixels, float_pixels)
|
||||
|
||||
self._texture = gpu.types.GPUTexture((width, height), format='SRGB8_A8', data=buffer)
|
||||
else:
|
||||
# bgl path for Blender < 3.0
|
||||
if HAS_BGL:
|
||||
# Fast path: bgl with GL_UNSIGNED_BYTE, no float conversion, in-place update
|
||||
if not hasattr(self, '_gl_texture_buf') or self._gl_texture_buf[0] == 0:
|
||||
self._gl_texture_buf = bgl.Buffer(bgl.GL_INT, 1)
|
||||
bgl.glGenTextures(1, self._gl_texture_buf)
|
||||
@ -817,12 +812,43 @@ class KromViewportEngine(bpy.types.RenderEngine):
|
||||
pixel_buffer = bgl.Buffer(bgl.GL_UNSIGNED_BYTE, num_pixels, bytes(pixel_array))
|
||||
|
||||
internal_format = getattr(bgl, 'GL_SRGB8_ALPHA8', bgl.GL_RGBA8)
|
||||
bgl.glTexImage2D(bgl.GL_TEXTURE_2D, 0, internal_format, width, height, 0,
|
||||
bgl.GL_RGBA, bgl.GL_UNSIGNED_BYTE, pixel_buffer)
|
||||
if self._tex_width == width and self._tex_height == height:
|
||||
bgl.glTexSubImage2D(bgl.GL_TEXTURE_2D, 0, 0, 0, width, height,
|
||||
bgl.GL_RGBA, bgl.GL_UNSIGNED_BYTE, pixel_buffer)
|
||||
else:
|
||||
bgl.glTexImage2D(bgl.GL_TEXTURE_2D, 0, internal_format, width, height, 0,
|
||||
bgl.GL_RGBA, bgl.GL_UNSIGNED_BYTE, pixel_buffer)
|
||||
bgl.glBindTexture(bgl.GL_TEXTURE_2D, 0)
|
||||
|
||||
self._texture = tex_id
|
||||
elif HAS_GPU_TEXTURE:
|
||||
# Blender 4.x+ path: use bpy.Image + gpu.texture.from_image
|
||||
# from_image returns a cached GPUTexture (no 20ms constructor)
|
||||
# pixels updated via foreach_set (fast C-level copy) + update()
|
||||
if self._viewport_image is None or self._viewport_image.size[0] != width or self._viewport_image.size[1] != height:
|
||||
if self._viewport_image is not None:
|
||||
bpy.data.images.remove(self._viewport_image)
|
||||
self._viewport_image = bpy.data.images.new(
|
||||
"_lnx_viewport", width, height, float_buffer=False)
|
||||
self._float_buffer = None
|
||||
|
||||
if HAS_NUMPY:
|
||||
pixels[3::4] = 255
|
||||
if self._float_buffer is None or len(self._float_buffer) != num_pixels:
|
||||
self._float_buffer = np.empty(num_pixels, dtype=np.float32)
|
||||
self._float_buffer[:num_pixels] = pixels[:num_pixels]
|
||||
self._float_buffer *= (1.0 / 255.0)
|
||||
self._viewport_image.pixels.foreach_set(self._float_buffer)
|
||||
else:
|
||||
pixel_array = array.array('B', pixels[:num_pixels])
|
||||
for i in range(3, len(pixel_array), 4):
|
||||
pixel_array[i] = 255
|
||||
float_pixels = [p / 255.0 for p in pixel_array]
|
||||
self._viewport_image.pixels.foreach_set(float_pixels)
|
||||
|
||||
self._viewport_image.update()
|
||||
self._texture = gpu.texture.from_image(self._viewport_image)
|
||||
|
||||
self._tex_width = width
|
||||
self._tex_height = height
|
||||
|
||||
@ -838,11 +864,17 @@ class KromViewportEngine(bpy.types.RenderEngine):
|
||||
"""Called by Blender when the engine instance is destroyed."""
|
||||
self._stop_krom_process()
|
||||
self._restore_overlay()
|
||||
if not HAS_GPU_TEXTURE and hasattr(self, '_gl_texture_buf') and self._gl_texture_buf[0] != 0:
|
||||
if HAS_BGL and hasattr(self, '_gl_texture_buf') and self._gl_texture_buf[0] != 0:
|
||||
try:
|
||||
bgl.glDeleteTextures(1, self._gl_texture_buf)
|
||||
except:
|
||||
pass
|
||||
if self._viewport_image is not None:
|
||||
try:
|
||||
bpy.data.images.remove(self._viewport_image)
|
||||
except:
|
||||
pass
|
||||
self._viewport_image = None
|
||||
if self._viewport_id and self._viewport_id in _active_krom_engines:
|
||||
del _active_krom_engines[self._viewport_id]
|
||||
|
||||
@ -928,6 +960,7 @@ class KromViewportEngine(bpy.types.RenderEngine):
|
||||
self._last_region_dims = dims
|
||||
self._request_resize(region.width, region.height)
|
||||
|
||||
self._write_camera_matrices(context)
|
||||
self._read_krom_camera(context)
|
||||
|
||||
pixels = self._read_framebuffer()
|
||||
@ -966,7 +999,8 @@ class KromViewportEngine(bpy.types.RenderEngine):
|
||||
self._batch_dims = dims
|
||||
|
||||
self._shader.bind()
|
||||
if HAS_GPU_TEXTURE:
|
||||
_is_gl_texture = isinstance(self._texture, int)
|
||||
if not _is_gl_texture:
|
||||
self._shader.uniform_sampler("image", self._texture)
|
||||
else:
|
||||
bgl.glActiveTexture(bgl.GL_TEXTURE0)
|
||||
@ -975,7 +1009,7 @@ class KromViewportEngine(bpy.types.RenderEngine):
|
||||
|
||||
self._batch.draw(self._shader)
|
||||
|
||||
if not HAS_GPU_TEXTURE:
|
||||
if _is_gl_texture:
|
||||
bgl.glBindTexture(bgl.GL_TEXTURE_2D, 0)
|
||||
|
||||
if HAS_GPU_STATE:
|
||||
|
||||
@ -875,7 +875,7 @@ def check_blender_version(op: bpy.types.Operator):
|
||||
"""Check whether the Blender version is supported by Leenkx,
|
||||
if not, report in UI.
|
||||
"""
|
||||
if bpy.app.version[:2] not in [(4, 5), (4, 4), (4, 2), (3, 6), (3, 3)]:
|
||||
if bpy.app.version[:2] not in [(5, 2), (4, 5), (4, 4), (4, 2), (3, 6), (3, 3)]:
|
||||
op.report({'INFO'}, 'INFO: For Leenkx to work correctly, use a Blender LTS version')
|
||||
|
||||
|
||||
|
||||
@ -835,6 +835,18 @@ def write_compiledglsl(defs, make_variants):
|
||||
if '_SSRefraction' in wrd.world_defs or '_VoxelRefract' in wrd.world_defs:
|
||||
f.write(f'#define GBUF_IDX_REFRACTION {idx_refraction}\n')
|
||||
|
||||
idx_anisotropy = idx_refraction
|
||||
if '_Anisotropy' in wrd.world_defs:
|
||||
f.write(f'#define GBUF_IDX_3 {idx_anisotropy}\n')
|
||||
|
||||
ext_brdf_defs = ('_ClearCoat', '_Sheen', '_Anisotropy', '_Subsurface', '_Transmission')
|
||||
if any(d in wrd.world_defs for d in ext_brdf_defs):
|
||||
f.write('#ifndef _ExtBRDF\n')
|
||||
f.write('#define _ExtBRDF\n')
|
||||
f.write('#endif\n')
|
||||
f.write('#define MAX_MATERIALS 16\n')
|
||||
f.write('uniform vec4 materialParams[MAX_MATERIALS * 8];\n')
|
||||
|
||||
f.write("""#if defined(HLSL) || defined(METAL)
|
||||
#define _InvY
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user