From 64209212a9d3f53468b33f82ffe4003ebf8b9b5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=B5=D0=BB=D0=B8=D0=BF=D0=B5=D0=BD=D0=BA=D0=BE=20?= =?UTF-8?q?=D0=98=D0=B2=D0=B0=D0=BD?= Date: Sat, 14 Dec 2019 12:38:21 +0000 Subject: [PATCH] git-svn-id: svn://db.shs.com.ru/libs@702 a8b55f48-bf90-11e4-a774-851b48703e85 --- qglengine/coeffs_brdf.png | Bin 0 -> 32788 bytes qglengine/qglview.qrc | 1 + qglengine/renderer.cpp | 2 +- qglengine/renderer_base.cpp | 100 ++++++++++++++++++++++++++++++-- qglengine/renderer_base.h | 2 +- qglengine/shaders/ds_light.glsl | 37 ++++++++++-- 6 files changed, 131 insertions(+), 11 deletions(-) create mode 100644 qglengine/coeffs_brdf.png diff --git a/qglengine/coeffs_brdf.png b/qglengine/coeffs_brdf.png new file mode 100644 index 0000000000000000000000000000000000000000..ec34d285dc6b032aeb6e469d9eb91ae8d8438449 GIT binary patch literal 32788 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�m$5COulAtSlazvyA z%1R-r1KZrrJAJZj7w`cm;9-IaDV65If`<{{kV-ngoRu_Jk z@lXB<{Qjm^0oEnA=Mz%F@g(4LRQ>r&<1hR&W3-Mt%L;zvIKLlQkAC@0f05;^cmdhz$K^zYa2FK#tp{g*I;a6)$+&-=yW(eifmHvZwx(%+T*`!xhu z|NWo;tu{6vAF*=el;%^9d{|fISo^Qbzg=yQ_!fR4?Dxz0^*@MS|LdRs_5aKN^AFJm z`jh{+ZS;p}BYH9Y*$xjp@)=+tY_#q42l@YQgq{BEwC%LLPk2AxY5PN9`t$G4Kf$!6 zkKgB<50Zn#*Ks5daJ=NbN12`g{`|M?g!3SMx*az + 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)); }