Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@
import com.jme3.texture.TextureCubeMap;
import com.jme3.texture.image.ColorSpace;
import com.jme3.util.BufferUtils;

/**
* Render the environment into a cubemap
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,20 @@ public TextureCubeMap getIrradiance() {
return irradiance;
}

/**
* Runtime samples the prefiltered map with sqrt(roughness) to recover a
* normalized mip coordinate. Bake-time therefore needs the inverse mapping:
* roughness = mipNorm^2.
*/
private float roughnessFromMip(int mip) {
int mipCount = specular.getImage().getMipMapSizes().length;
if (mipCount <= 1) {
return 0f;
}
float mipNorm = (float) mip / (float) (mipCount - 1);
return mipNorm * mipNorm;
}

private void bakeSpecularIBL(int mip, float roughness, Material mat, Geometry screen) throws Exception {
mat.setFloat("Roughness", roughness);

Expand Down Expand Up @@ -171,7 +185,7 @@ public void bakeSpecularIBL() {
int mip = 0;
for (; mip < specular.getImage().getMipMapSizes().length; mip++) {
try {
float roughness = (float) mip / (float) (specular.getImage().getMipMapSizes().length - 1);
float roughness = roughnessFromMip(mip);
bakeSpecularIBL(mip, roughness, mat, screen);
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Error while computing mip level " + mip, e);
Expand All @@ -187,7 +201,7 @@ public void bakeSpecularIBL() {
specular.getImage().setMipmapsGenerated(true);
if (sizes.length <= 1) {
try {
LOGGER.log(Level.WARNING, "Workaround driver BUG: only one mip level available, regenerate it with higher roughness (shiny fix)");
LOGGER.log(Level.WARNING, "Workaround driver BUG: only one mip level is usable, regenerate mip 0 with roughness 1 to avoid an overly shiny fallback");
bakeSpecularIBL(0, 1f, mat, screen);
} catch (Exception e) {
LOGGER.log(Level.FINE, "Error while recomputing mip level 0", e);
Expand Down Expand Up @@ -293,4 +307,4 @@ public void bakeIrradiance() {

}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,20 @@ public boolean isTexturePulling() { // always pull textures from gpu
return true;
}

/**
* Runtime samples the prefiltered map with sqrt(roughness) to recover a
* normalized mip coordinate. Bake-time therefore needs the inverse mapping:
* roughness = mipNorm^2.
*/
private float roughnessFromMip(int mip) {
int mipCount = specular.getImage().getMipMapSizes().length;
if (mipCount <= 1) {
return 0f;
}
float mipNorm = (float) mip / (float) (mipCount - 1);
return mipNorm * mipNorm;
}

private void bakeSpecularIBL(int mip, float roughness, Material mat, Geometry screen) throws Exception {
mat.setFloat("Roughness", roughness);

Expand Down Expand Up @@ -161,7 +175,7 @@ public void bakeSpecularIBL() {
int mip = 0;
for (; mip < specular.getImage().getMipMapSizes().length; mip++) {
try {
float roughness = (float) mip / (float) (specular.getImage().getMipMapSizes().length - 1);
float roughness = roughnessFromMip(mip);
bakeSpecularIBL(mip, roughness, mat, screen);
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Error while computing mip level " + mip, e);
Expand All @@ -177,7 +191,7 @@ public void bakeSpecularIBL() {
specular.getImage().setMipmapsGenerated(true);
if (sizes.length <= 1) {
try {
LOGGER.log(Level.WARNING, "Workaround driver BUG: only one mip level available, regenerate it with higher roughness (shiny fix)");
LOGGER.log(Level.WARNING, "Workaround driver BUG: only one mip level is usable, regenerate mip 0 with roughness 1 to avoid an overly shiny fallback");
bakeSpecularIBL(0, 1f, mat, screen);
} catch (Exception e) {
LOGGER.log(Level.FINE, "Error while recomputing mip level 0", e);
Expand Down Expand Up @@ -207,4 +221,4 @@ public void bakeSphericalHarmonicsCoefficients() {
public Vector3f[] getSphericalHarmonicsCoefficients() {
return shCoef;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,13 @@ private static boolean generateTangents(Mesh mesh) {
case Triangles:
case TriangleFan:
case TriangleStrip:
case Patch:
hasTriangles = true;
break;

case Patch:
logger.log(Level.SEVERE, "Tangent generation does not support mesh mode={0}", mode);
return false;

default:
logger.log(Level.SEVERE, "Tangent generation isn't implemented for mode={0}", mode);
return false;
Expand Down Expand Up @@ -614,6 +617,7 @@ static void generateSharedVerticesIndexListSlow(int piTriList_in_and_out[], fina

if (vP.equals(vP2) && vN.equals(vN2) && vT.equals(vT2)) {
bFound = true;
index2rec = index2;
} else {
++j;
}
Expand Down Expand Up @@ -662,6 +666,7 @@ static int generateInitialVerticesIndexList(TriInfo pTriInfos[], int piTriList_o
//Note, Nehon: we should never get there with JME, because we don't support quads...
//but I'm going to let it there in case someone needs it... Just know this code is not tested.
{//TODO remove those useless brackets...
pTriInfos[iDstTriIndex + 1] = new TriInfo();
pTriInfos[iDstTriIndex + 1].orgFaceNumber = f;
pTriInfos[iDstTriIndex + 1].tSpacesOffs = iTSpacesOffs;
}
Expand Down Expand Up @@ -1368,6 +1373,9 @@ static void buildNeighborsFast(TriInfo pTriInfos[], Edge[] pEdges, final int piT
quickSortEdges(pEdges, iL, iR, 1, uSeed); // sort channel 1 which is i1
}
}
if (iEntries > 0) {
quickSortEdges(pEdges, iCurStartIndex, iEntries - 1, 1, uSeed);
}

// sub sort over f, which should be fast.
// this step is to remain compliant with BuildNeighborsSlow() when
Expand All @@ -1382,6 +1390,9 @@ static void buildNeighborsFast(TriInfo pTriInfos[], Edge[] pEdges, final int piT
quickSortEdges(pEdges, iL, iR, 2, uSeed); // sort channel 2 which is f
}
}
if (iEntries > 0) {
quickSortEdges(pEdges, iCurStartIndex, iEntries - 1, 2, uSeed);
}

// pair up, adjacent triangles
for (int i = 0; i < iEntries; i++) {
Expand Down
21 changes: 8 additions & 13 deletions jme3-core/src/main/resources/Common/IBL/IBLKernels.frag
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ uniform int m_FaceId;

void brdfKernel(){
float NdotV=TexCoords.x;
float m_Roughness=TexCoords.y;
float roughness=TexCoords.y;
float alpha = roughness * roughness;

vec3 V;
V.x = sqrt(1.0 - NdotV*NdotV);
Expand All @@ -28,13 +29,13 @@ void brdfKernel(){
const uint SAMPLE_COUNT = 1024u;
for(uint i = 0u; i < SAMPLE_COUNT; i++){
vec4 Xi = Hammersley(i, SAMPLE_COUNT);
vec3 H = ImportanceSampleGGX(Xi, m_Roughness, N);
vec3 H = ImportanceSampleGGX(Xi, alpha, N);
vec3 L = normalize(2.0 * dot(V, H) * H - V);
float NdotL = max(L.z, 0.0);
float NdotH = max(H.z, 0.0);
float VdotH = max(dot(V, H), 0.0);
if(NdotL > 0.0){
float G = GeometrySmith(N, V, L, m_Roughness*m_Roughness);
float G = GeometrySmith(N, V, L, alpha);
float G_Vis = (G * VdotH) / (NdotH * NdotV);
float Fc = pow(1.0 - VdotH, 5.0);
A += (1.0 - Fc) * G_Vis;
Expand Down Expand Up @@ -75,26 +76,20 @@ void prefilteredEnvKernel(){
vec3 R = N;
vec3 V = R;

float a2 = m_Roughness * m_Roughness;
float roughness = clamp(m_Roughness, 0.0, 1.0);
float alpha = roughness * roughness;

const uint SAMPLE_COUNT = 1024u;
float totalWeight = 0.0;
vec3 prefilteredColor = vec3(0.0);
for(uint i = 0u; i < SAMPLE_COUNT; ++i) {
vec4 Xi = Hammersley(i, SAMPLE_COUNT);
vec3 H = ImportanceSampleGGX(Xi, a2, N);
vec3 H = ImportanceSampleGGX(Xi, alpha, N);
float VoH = max(dot(V, H), 0.0);
vec3 L = normalize(2.0 * VoH * H - V);
float NdotL = max(dot(N, L), 0.0);
if(NdotL > 0.0) {
vec3 sampleColor = texture(m_EnvMap, L).rgb;

float luminance = dot(sampleColor, vec3(0.2126, 0.7152, 0.0722));
if (luminance > 64.0) { // TODO use average?
sampleColor *= 64.0/luminance;
}

// TODO: use mipmap
prefilteredColor += sampleColor * NdotL;
totalWeight += NdotL;
}
Expand All @@ -113,4 +108,4 @@ void main(){
#else
brdfKernel();
#endif
}
}
54 changes: 38 additions & 16 deletions jme3-core/src/main/resources/Common/IBL/Math.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -51,45 +51,67 @@ vec4 Hammersley(uint i, uint N){
// }


vec3 ImportanceSampleGGX(vec4 Xi, float a2, vec3 N){

float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a2 - 1.0) * Xi.y));
// Shared roughness convention for the IBL bake path:
// roughness = perceptual roughness in [0, 1]
// alpha = roughness * roughness
// alpha2 = alpha * alpha
//
// ImportanceSampleGGX() and GeometrySmith() both expect alpha.
const float MIN_GGX_ALPHA = 0.0064;

float SafeGGXAlpha(float alpha) {
return max(alpha, MIN_GGX_ALPHA);
}

vec3 ImportanceSampleGGX(vec4 Xi, float alpha, vec3 N){
alpha = SafeGGXAlpha(alpha);
float alpha2 = alpha * alpha;
float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (alpha2 - 1.0) * Xi.y));
float sinTheta = sqrt(1.0 - cosTheta*cosTheta);

// from spherical coordinates to cartesian coordinates
vec3 H;
H.x = Xi.z * sinTheta;
H.y = Xi.w * sinTheta;
H.z = cosTheta;

// from tangent-space vector to world-space sample vector
vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
vec3 tangent = normalize(cross(up, N));
vec3 bitangent = cross(N, tangent);

vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
return normalize(sampleVec);
}

}

float DistributionGGX(float NdotH, float alpha) {
alpha = SafeGGXAlpha(alpha);
float alpha2 = alpha * alpha;
float denom = (NdotH * NdotH) * (alpha2 - 1.0) + 1.0;
return alpha2 / (PI * denom * denom);
}

float ImportanceSampleGGXPdf(float NdotH, float VdotH, float alpha) {
float D = DistributionGGX(NdotH, alpha);
return max((D * NdotH) / max(4.0 * VdotH, 1e-4), 0.0);
}

float GeometrySchlickGGX(float NdotV, float roughness){
float a = roughness;
float k = (a * a) / 2.0;
float GeometrySchlickGGX(float NdotV, float alpha){
alpha = SafeGGXAlpha(alpha);
float k = alpha / 2.0;

float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;

return nom / denom;
}
// ----------------------------------------------------------------------------
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness){
float GeometrySmith(vec3 N, vec3 V, vec3 L, float alpha){
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
float ggx2 = GeometrySchlickGGX(NdotV, alpha);
float ggx1 = GeometrySchlickGGX(NdotL, alpha);

return ggx1 * ggx2;
}

}

45 changes: 29 additions & 16 deletions jme3-core/src/main/resources/Common/ShaderLib/PBR.glsllib
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
#define NB_PROBES 0
#endif

// Internal GGX stability floor. This does not change the user-facing
// roughness convention or probe LOD mapping; it only prevents extremely sharp
// microfacet lobes from reaching pathological peaks that can destabilize some
// drivers and screenshot-test software renderers.
const float MIN_GGX_ALPHA = 0.0064;

// BEGIN-@jhonkkk,Specular AA --------------------------------------------------------------
// see:http://www.jp.square-enix.com/tech/library/pdf/ImprovedGeometricSpecularAA(slides).pdf
// https://docs.unity3d.com/Packages/com.unity.render-pipelines.high-definition@15.0/manual/Geometric-Specular-Anti-Aliasing.html
Expand Down Expand Up @@ -55,27 +61,27 @@ float Inner_PBR_ComputeDirectLight(
vec3 normal, vec3 halfVec, vec3 lightDir, vec3 viewDir,
vec3 lightColor, vec3 fZero, float alpha, float ndotv,
out vec3 outDiffuse, out vec3 outSpecular){
alpha = max(alpha, MIN_GGX_ALPHA);

// Compute ndotl, ndoth, vdoth terms which are needed later.
float ndotl = max( dot(normal, lightDir), 0.0);
float ndoth = max( dot(normal, halfVec), 0.0);
float hdotv = max( dot(viewDir, halfVec), 0.0);

// Compute diffuse using energy-conserving Lambert.
// Alternatively, use Oren-Nayar for really rough
// materials or if you have lots of processing power ...
outDiffuse = vec3(ndotl) * lightColor;

//cook-torrence, microfacet BRDF : http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf

//D, GGX normal Distribution function
float alpha2 = alpha * alpha;
float sum = ((ndoth * ndoth) * (alpha2 - 1.0) + 1.0);
float denom = PI * sum * sum;
float D = alpha2 / denom;
float D = alpha2 / denom;

// Compute Fresnel function via Schlick's approximation.
vec3 fresnel = F_Shlick(hdotv, fZero);
// Lambert diffuse BRDF with a Fresnel-based energy split.
// The caller multiplies this term by diffuseColor, which already encodes
// the metallic workflow's (1 - metallic) factor.
outDiffuse = vec3(ndotl / PI) * lightColor * (vec3(1.0) - fresnel);

//G Schlick GGX Geometry shadowing term, k = alpha/2
float k = alpha * 0.5;
Expand Down Expand Up @@ -136,22 +142,32 @@ vec3 integrateBRDFApprox( const in vec3 specular, float roughness, float NoV ){
return specular * AB.x + AB.y;
}

float computeSpecularAO(float ao, float roughness, float NoV) {
float exponent = exp2(-16.0 * roughness - 1.0);
return clamp(pow(NoV + ao, exponent) - 1.0 + ao, 0.0, 1.0);
}

// from Sebastien Lagarde https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf page 69
vec3 getSpecularDominantDir(const in vec3 N, const in vec3 R, const in float realRoughness){
// The Frostbite note's "real roughness" here is the microfacet alpha term,
// not perceptual roughness. Callers should pass alpha = roughness * roughness.
vec3 getSpecularDominantDir(const in vec3 N, const in vec3 R, const in float alpha){
vec3 dominant;

float smoothness = 1.0 - realRoughness;
float lerpFactor = smoothness * (sqrt(smoothness) + realRoughness);
float smoothness = 1.0 - alpha;
float lerpFactor = smoothness * (sqrt(smoothness) + alpha);
// The result is not normalized as we fetch in a cubemap
dominant = mix(N, R, lerpFactor);

return dominant;
}

vec3 ApproximateSpecularIBL(samplerCube envMap,sampler2D integrateBRDF, vec3 SpecularColor , float Roughness, float ndotv, vec3 refVec, float nbMipMaps){
// The specular bake stores roughness quadratically across mip levels:
// mipNorm = mip / (nbMipMaps - 1), roughness = mipNorm * mipNorm.
// Runtime therefore samples with sqrt(roughness) to recover mipNorm.
float Lod = sqrt( Roughness ) * (nbMipMaps - 1.0);
vec3 PrefilteredColor = textureCubeLod(envMap, refVec.xyz,Lod).rgb;
vec2 EnvBRDF = texture2D(integrateBRDF,vec2(Roughness, ndotv)).rg;
vec2 EnvBRDF = texture2D(integrateBRDF,vec2(ndotv, Roughness)).rg;
return PrefilteredColor * ( SpecularColor * EnvBRDF.x+ EnvBRDF.y );
}

Expand Down Expand Up @@ -242,13 +258,10 @@ float renderProbe(vec3 viewDir, vec3 worldPos, vec3 normal, vec3 norm, float Rou
indirectSpecular *= vec3(horiz);
#endif

vec3 indirectLighting = (indirectDiffuse + indirectSpecular) * ao;
float diffuseAO = clamp(ao.r, 0.0, 1.0);
float specularAO = computeSpecularAO(diffuseAO, Roughness, ndotv);
vec3 indirectLighting = indirectDiffuse * diffuseAO + indirectSpecular * specularAO;

color = indirectLighting * step( 0.0, probePos.w);
return ndf;
}





Loading
Loading