diff --git a/qglengine/coeffs_brdf.png b/qglengine/coeffs_brdf.png new file mode 100644 index 0000000..ec34d28 Binary files /dev/null and b/qglengine/coeffs_brdf.png differ diff --git a/qglengine/qglview.qrc b/qglengine/qglview.qrc index 6ac2fb6..e67f617 100644 --- a/qglengine/qglview.qrc +++ b/qglengine/qglview.qrc @@ -1,5 +1,6 @@ + coeffs_brdf.png icons/add-type-camera.png icons/add-type-geo.png icons/add-type-light.png diff --git a/qglengine/renderer.cpp b/qglengine/renderer.cpp index a7f0aaa..3d2221d 100644 --- a/qglengine/renderer.cpp +++ b/qglengine/renderer.cpp @@ -156,7 +156,7 @@ void Renderer::initShaders() { if (!bindShader(roles[p], &prog)) continue; for (int i = 0; i < 5; ++i) prog->setUniformValue(QString("tex_%1").arg(i).toLatin1().constData(), i); - //prog->setUniformValue("tex_coeffs[0]", (int)Renderer::dbrBuffersCount); + prog->setUniformValue("tex_coeffs[0]", (int)Renderer::dbrBuffersCount); //prog->setUniformValue("tex_coeffs[1]", (int)Renderer::dbrBuffersCount+1); } if (bindShader(srFinalPass, &prog)) { diff --git a/qglengine/renderer_base.cpp b/qglengine/renderer_base.cpp index 5f63039..5d0782b 100644 --- a/qglengine/renderer_base.cpp +++ b/qglengine/renderer_base.cpp @@ -301,7 +301,93 @@ void RendererBase::renderQuad(QOpenGLShaderProgram * prog, Mesh * mesh, Camera * } + + +float RadicalInverse_VdC(uint bits) { + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return float(bits) * 2.3283064365386963e-10; // / 0x100000000 +} +// ---------------------------------------------------------------------------- +QVector2D Hammersley(uint i, uint N) { + return QVector2D(float(i)/float(N), RadicalInverse_VdC(i)); +} +QVector3D ImportanceSampleGGX(QVector2D Xi, QVector3D N, float roughness) { + float a = roughness*roughness; + float phi = 2.0 * M_PI * Xi[0]; + float cosTheta = sqrt((1.0 - Xi[1]) / (1.0 + (a*a - 1.0) * Xi[1])); + float sinTheta = sqrt(1.0 - cosTheta*cosTheta); + // преобразование из сферических в декартовы координаты + QVector3D H; + H[0] = cos(phi) * sinTheta; + H[1] = sin(phi) * sinTheta; + H[2] = cosTheta; + // преобразование из касательного пространства в мировые координаты + QVector3D up = qAbs(N[2]) < 0.999 ? QVector3D(0.0, 0.0, 1.0) : QVector3D(1.0, 0.0, 0.0); + QVector3D tangent = QVector3D::crossProduct(up, N).normalized(); + QVector3D bitangent = QVector3D::crossProduct(N, tangent); + QVector3D sampleVec = tangent * H[0] + bitangent * H[1] + N * H[2]; + return sampleVec.normalized(); +} +float GeometrySchlickGGX(float NdotV, float roughness) { + float k = (roughness * roughness) / 2.0; + float nom = NdotV; + float denom = NdotV * (1.0 - k) + k; + return nom / denom; +} +float GeometrySmith(QVector3D N, QVector3D V, QVector3D L, float roughness) { + float NdotV = piMax(QVector3D::dotProduct(N, V), 0.f); + float NdotL = piMax(QVector3D::dotProduct(N, L), 0.f); + float ggx2 = GeometrySchlickGGX(NdotV, roughness); + float ggx1 = GeometrySchlickGGX(NdotL, roughness); + return ggx1 * ggx2; +} +QVector2D IntegrateBRDF(float NdotV, float roughness) { + QVector3D V; + V[0] = sqrt(1.f - NdotV*NdotV); + V[1] = 0.f; + V[2] = NdotV; + float A = 0.f; + float B = 0.f; + QVector3D N = QVector3D(0.f, 0.f, 1.f); + const uint SAMPLE_COUNT = 256u; + for(uint i = 0u; i < SAMPLE_COUNT; ++i) { + QVector2D Xi = Hammersley(i, SAMPLE_COUNT); + QVector3D H = ImportanceSampleGGX(Xi, N, roughness); + QVector3D L = (2.f * QVector3D::dotProduct(V, H) * H - V).normalized(); + float NdotL = piMax(L[2], 0.f); + float NdotH = piMax(H[2], 0.f); + float VdotH = piMax(QVector3D::dotProduct(V, H), 0.f); + if(NdotL > 0.f) { + float G = GeometrySmith(N, V, L, roughness); + float G_Vis = (G * VdotH) / (NdotH * NdotV); + float Fc = pow(1.f - VdotH, 5.f); + A += (1.f - Fc) * G_Vis; + B += Fc * G_Vis; + } + } + A /= float(SAMPLE_COUNT); + B /= float(SAMPLE_COUNT); + return QVector2D(A, B); +} + void RendererBase::initCoeffTextures() { + QImage im = QImage(":/coeffs_brdf.png").mirrored(); + int size = im.width(); + QVector data(size*size); + int ind = -1; + for (int x = 0; x < size; ++x) { + //float c = x / double(size - 1) + 1.E-3; + for (int y = 0; y < size; ++y) { + //float r = y / double(size - 1); + QColor p = im.pixelColor(x, y); + data[++ind] = QVector2D(p.redF(), p.greenF());//IntegrateBRDF(c, 1.f - r + 1.E-3); + } + } + createCoeffTexture(tex_coeff[0], data.constData(), size, 2); /*const int size = 512; QVector data_diff(size*size), data_spec(size*size); double r, c, c2; @@ -334,15 +420,19 @@ void RendererBase::initCoeffTextures() { } -void RendererBase::createCoeffTexture(GLuint & id, const QVector & data, int size) { +void RendererBase::createCoeffTexture(GLuint & id, const void * data, int size, int channels) { QOpenGLExtraFunctions * f = view; deleteGLTexture(f, id); f->glGenTextures(1, &id); f->glBindTexture(GL_TEXTURE_2D, id); - f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE ); + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - f->glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, size, size, 0, GL_RED, GL_FLOAT, data.constData()); + GLenum iformat = GL_R16F, format = GL_RED; + if (channels == 2) {iformat = GL_RG16F; format = GL_RG;} + if (channels == 3) {iformat = GL_RGB16F; format = GL_RGB;} + if (channels == 4) {iformat = GL_RGBA16F; format = GL_RGBA;} + f->glTexImage2D(GL_TEXTURE_2D, 0, iformat, size, size, 0, format, GL_FLOAT, data); } diff --git a/qglengine/renderer_base.h b/qglengine/renderer_base.h index 9cb4370..d3fd1a4 100644 --- a/qglengine/renderer_base.h +++ b/qglengine/renderer_base.h @@ -46,7 +46,7 @@ protected: void initQuad(Mesh * mesh, QMatrix4x4 mat = QMatrix4x4()); void renderQuad(QOpenGLShaderProgram * prog, Mesh * mesh, Camera * cam = 0, bool uniforms = true); void initCoeffTextures(); - void createCoeffTexture(GLuint & id, const QVector & data, int size); + void createCoeffTexture(GLuint & id, const void * data, int size, int channels = 1); QGLView * view; TextureManager * textures_manager; diff --git a/qglengine/shaders/ds_light.glsl b/qglengine/shaders/ds_light.glsl index ebb79b2..e072de0 100644 --- a/qglengine/shaders/ds_light.glsl +++ b/qglengine/shaders/ds_light.glsl @@ -95,6 +95,26 @@ void calcLight(in int index, in vec3 n, in vec3 v) { si += spot * spec * qgl_light_parameter[index].color.rgb; } + + +float GeometrySchlickGGX(float NdotV, float roughness) { + float a = roughness; + float k = (a * a) / 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 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); + return ggx1 * ggx2; +} + + + void main(void) { ivec2 tc = ivec2(gl_FragCoord.xy); vec4 v1 = texelFetch(tex_1, tc, 0); @@ -118,11 +138,13 @@ void main(void) { float metalness = v2.r; float roughness = v2.g; float reflectivity = v2.b; + float NdotV = dot(normal, v); + float roughness3 = roughness*roughness*roughness; //bn = normalize(vec3(v3.w, v4.zw)); //bn2 = normalize(cross(n, bn)); rough_diff = max(roughness, _min_rough); - rough_spec = max(roughness*roughness*roughness, _min_rough); - float shlick = clamp(metalness + (1 - metalness) * pow(1 - dot(normal, v), 5), 0, 1); + rough_spec = max(roughness3, _min_rough); + float shlick = clamp(metalness + (1 - metalness) * pow(1 - NdotV, 5), 0, 1); li = vec3(0.);//qgl_AmbientLight.color.rgb * qgl_AmbientLight.intensity; si = vec3(0.); @@ -131,7 +153,12 @@ void main(void) { si *= shlick; li *= (1 - shlick); alpha = min(1, alpha * (1 + shlick)); + + vec2 brdf = texture(tex_coeffs[0], vec2(NdotV*0.99, roughness3*0.995)).rg; + float env_spec = shlick * brdf.x + brdf.y; + vec3 res_col = max(vec3(0), li * diffuse + si * mix(vec3(1), diffuse, metalness) + emission); + res_col = mix(res_col, fog_color.rgb, env_spec); float plen = length(pos.xyz); float fog = 1 - exp(-plen / fog_decay); @@ -140,6 +167,8 @@ void main(void) { qgl_FragColor = vec4(res_col, alpha); - //qgl_FragColor.rgb = vec3(texture(tex_coeffs[0], qgl_FragTexture.xy).r); - //qgl_FragColor.rgb = vec3(ldir); + //vec3 specular = prefilteredColor * (F * envBRDF.x + envBRDF.y); + //qgl_FragColor.rgb = vec3(shlick * brdf.x + brdf.y); + //qgl_FragColor.rgb = vec3(env_spec); + //qgl_FragColor.rgb = vec3(1-GeometrySchlickGGX(dot(normal, v),roughness)); }