Skip to content

Commit

Permalink
Using 6 textures to make cubemap on low version OpenGL
Browse files Browse the repository at this point in the history
  • Loading branch information
huxingyi committed Sep 23, 2022
1 parent ddcdfeb commit d512ec0
Show file tree
Hide file tree
Showing 10 changed files with 314 additions and 34 deletions.
5 changes: 5 additions & 0 deletions ACKNOWLEDGEMENTS.html
Expand Up @@ -1375,3 +1375,8 @@ <h1>Ahmad Abdul Karim, Alexandre Meyer, Thibaut Gaudin, Axel Buendia and Saida B
Generic Spine Model with Simple Physics for Life-Like Quadrupeds and Reptiles
https://www.ahmadabdulkarim.com/?page_id=16
</pre>

<h1>gman</h1>
<pre>
https://stackoverflow.com/questions/53115467/how-to-implement-texturecube-using-6-sampler2d
</pre>
77 changes: 77 additions & 0 deletions application/shaders/model.frag
@@ -0,0 +1,77 @@
#version 110
uniform vec3 eyePosition;
uniform sampler2D environmentIrradianceMapId[6];
uniform sampler2D environmentSpecularMapId[6];
varying vec3 pointPosition;
varying vec3 pointNormal;
varying vec3 pointColor;
varying float pointAlpha;
varying float pointMetalness;
varying float pointRoughness;

const float PI = 3.1415926;

vec3 fresnelSchlickRoughness(float NoV, vec3 f0, float roughness)
{
return f0 + (max(vec3(1.0 - roughness), f0) - f0) * pow(clamp(1.0 - NoV, 0.0, 1.0), 5.0);
}

void cubemap(vec3 r, out float texId, out vec2 st)
{
vec3 uvw;
vec3 absr = abs(r);
if (absr.x > absr.y && absr.x > absr.z) {
// x major
float negx = step(r.x, 0.0);
uvw = vec3(r.zy, absr.x) * vec3(mix(-1.0, 1.0, negx), -1, 1);
texId = negx;
} else if (absr.y > absr.z) {
// y major
float negy = step(r.y, 0.0);
uvw = vec3(r.xz, absr.y) * vec3(1.0, mix(1.0, -1.0, negy), 1.0);
texId = 2.0 + negy;
} else {
// z major
float negz = step(r.z, 0.0);
uvw = vec3(r.xy, absr.z) * vec3(mix(1.0, -1.0, negz), -1, 1);
texId = 4.0 + negz;
}
st = vec2(uvw.xy / uvw.z + 1.) * .5;
}

vec4 texturesAsCube(in sampler2D maps[6], in vec3 direction)
{
float texId;
vec2 st;
cubemap(direction, texId, st);
vec4 color = vec4(0);
for (int i = 0; i < 6; ++i) {
vec4 side = texture2D(maps[i], st);
float select = step(float(i) - 0.5, texId) * step(texId, float(i) + 0.5);
color = mix(color, side, select);
}
return color;
}

void main()
{
vec3 n = pointNormal;
vec3 v = normalize(eyePosition - pointPosition);
vec3 r = reflect(-v, n);

float NoV = abs(dot(n, v)) + 1e-5;

vec3 irradiance = texturesAsCube(environmentIrradianceMapId, r).rgb;
vec3 diffuse = irradiance * (1.0 - pointMetalness) * pointColor;

vec3 f0 = mix(vec3(0.04), pointColor, pointMetalness);
vec3 fresnelFactor = fresnelSchlickRoughness(NoV, f0, pointRoughness);
vec3 specular = fresnelFactor * texturesAsCube(environmentSpecularMapId, r).rgb;

vec3 color = diffuse + specular;

color = color / (color + vec3(1.0));
color = pow(color, vec3(1.0/2.2));

gl_FragColor = vec4(color, pointAlpha);
}
31 changes: 31 additions & 0 deletions application/shaders/model.vert
@@ -0,0 +1,31 @@
#version 110
attribute vec4 vertex;
attribute vec3 normal;
attribute vec3 color;
attribute vec2 texCoord;
attribute float metalness;
attribute float roughness;
attribute vec3 tangent;
attribute float alpha;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform vec3 eyePosition;
varying vec3 pointPosition;
varying vec3 pointNormal;
varying vec3 pointColor;
varying float pointAlpha;
varying float pointMetalness;
varying float pointRoughness;

void main()
{
pointPosition = (modelMatrix * vertex).xyz;
pointNormal = normalize((modelMatrix * vec4(normal, 1.0)).xyz);
pointColor = color;
pointAlpha = alpha;
pointMetalness = metalness;
pointRoughness = roughness;

gl_Position = projectionMatrix * viewMatrix * vec4(pointPosition, 1.0);
}
17 changes: 8 additions & 9 deletions application/shaders/model_core.frag
Expand Up @@ -12,9 +12,9 @@ out vec4 fragColor;

const float PI = 3.1415926;

vec3 fresnelFactor(const vec3 f0, float u)
vec3 fresnelSchlickRoughness(float NoV, vec3 f0, float roughness)
{
return mix(f0, vec3(1.0), pow(1.01 - u, 5.0));
return f0 + (max(vec3(1.0 - roughness), f0) - f0) * pow(clamp(1.0 - NoV, 0.0, 1.0), 5.0);
}

void main()
Expand All @@ -23,19 +23,18 @@ void main()
vec3 v = normalize(eyePosition - pointPosition);
vec3 r = reflect(-v, n);

vec3 diffuseColor = (1.0 - pointMetalness) * pointColor;

float NoV = abs(dot(n, v)) + 1e-5;

vec3 specular = mix(vec3(0.04), vec3(1.0), pointMetalness);

vec3 irradiance = texture(environmentIrradianceMapId, r).rgb;
vec3 diffuse = irradiance * diffuseColor / PI;
vec3 diffuse = irradiance * (1.0 - pointMetalness) * pointColor;

vec3 reflected = fresnelFactor(specular, NoV) * texture(environmentSpecularMapId, r, 1.0).rgb;
vec3 f0 = mix(vec3(0.04), pointColor, pointMetalness);
vec3 fresnelFactor = fresnelSchlickRoughness(NoV, f0, pointRoughness);
vec3 specular = fresnelFactor * texture(environmentSpecularMapId, r, 0.0).rgb;

vec3 color = diffuse + reflected * pointMetalness;
vec3 color = diffuse + specular;

color = color / (color + vec3(1.0));
color = pow(color, vec3(1.0/2.2));

fragColor = vec4(color, pointAlpha);
Expand Down
149 changes: 141 additions & 8 deletions application/sources/dds_file.cc
Expand Up @@ -301,7 +301,7 @@ static const char *DxgiFormatToString(DXGI_FORMAT dxgiFormat)
"DXGI_FORMAT_FORCE_UINT",
};
int index = (int)dxgiFormat;
if (index >= 0 && index < sizeof(names) / sizeof(names[0]))
if (index >= 0 && index < (int)(sizeof(names) / sizeof(names[0])))
return names[index];
return "(Unknown)";
}
Expand All @@ -324,7 +324,7 @@ static const char *ResourceDimensionToString(D3D10_RESOURCE_DIMENSION resourceDi
"D3D10_RESOURCE_DIMENSION_TEXTURE3D",
};
int index = (int)resourceDimension;
if (index >= 0 && index < sizeof(names) / sizeof(names[0]))
if (index >= 0 && index < (int)(sizeof(names) / sizeof(names[0])))
return names[index];
return "(Unknown)";
}
Expand All @@ -347,7 +347,7 @@ static const char *MiscFlagToString(UINT miscFlag)
"D3D10_RESOURCE_MISC_GDI_COMPATIBLE",
};
int index = (int)miscFlag;
if (index >= 0 && index < sizeof(names) / sizeof(names[0]))
if (index >= 0 && index < (int)(sizeof(names) / sizeof(names[0])))
return names[index];
return "(Unknown)";
}
Expand All @@ -371,6 +371,139 @@ DdsFileReader::DdsFileReader(const QString &filename) :
{
}

std::unique_ptr<std::vector<std::unique_ptr<QOpenGLTexture>>> DdsFileReader::createOpenGLTextures()
{
QFile file(m_filename);

if (!file.open(QIODevice::ReadOnly)) {
qDebug() << "Open" << m_filename << "failed";
return nullptr;
}

DDS_FILE_HEADER fileHeader;
if (sizeof(fileHeader) != file.read((char *)&fileHeader, sizeof(fileHeader))) {
qDebug() << "Read DDS file hader failed";
return nullptr;
}

if (0x20534444 != qFromLittleEndian<quint32>(&fileHeader.dwMagic)) {
qDebug() << "Not a DDS file";
return nullptr;
}

if (0x30315844 != qFromLittleEndian<quint32>(&fileHeader.header.ddspf.dwFourCC)) {
qDebug() << "Unsupported DDS file, expected DX10 file";
return nullptr;
}

auto caps2 = qFromLittleEndian<quint32>(&fileHeader.header.dwCaps2);
if (!(DDSCAPS2_CUBEMAP & caps2)) {
qDebug() << "Unsupported DDS file, expected CUBEMAP file";
return nullptr;
}

//qDebug() << "Start anyalize DDS file...";

int width = qFromLittleEndian<quint32>(&fileHeader.header.dwWidth);
int height = qFromLittleEndian<quint32>(&fileHeader.header.dwHeight);

//qDebug() << "DDS size:" << width << "X" << height;

//auto pitchOrLinearSize = qFromLittleEndian<quint32>(&fileHeader.header.dwPitchOrLinearSize);
//qDebug() << "DDS pitch or linear size:" << pitchOrLinearSize;

auto arraySize = qFromLittleEndian<quint32>(&fileHeader.header10.arraySize);
//qDebug() << "DDS array size:" << arraySize;

auto mipMapCount = qFromLittleEndian<quint32>(&fileHeader.header.dwMipMapCount);
//qDebug() << "DDS mip map count:" << mipMapCount;

DXGI_FORMAT dxgiFormat = (DXGI_FORMAT)qFromLittleEndian<quint32>(&fileHeader.header10.dxgiFormat);
//qDebug() << "DDS dxgi format:" << DxgiFormatToString(dxgiFormat);
//qDebug() << "DDS resource dimension:" << ResourceDimensionToString((D3D10_RESOURCE_DIMENSION)qFromLittleEndian<quint32>(&fileHeader.header10.resourceDimension));
//qDebug() << "DDS misc flag:" << MiscFlagToString((UINT)qFromLittleEndian<quint32>(&fileHeader.header10.miscFlag));

quint32 faces = 0;
if (DDSCAPS2_CUBEMAP_POSITIVEX & caps2) {
//qDebug() << "DDS found +x";
++faces;
}
if (DDSCAPS2_CUBEMAP_NEGATIVEX & caps2) {
//qDebug() << "DDS found -x";
++faces;
}
if (DDSCAPS2_CUBEMAP_POSITIVEY & caps2) {
//qDebug() << "DDS found +y";
++faces;
}
if (DDSCAPS2_CUBEMAP_NEGATIVEY & caps2) {
//qDebug() << "DDS found -y";
++faces;
}
if (DDSCAPS2_CUBEMAP_POSITIVEZ & caps2) {
//qDebug() << "DDS found +z";
++faces;
}
if (DDSCAPS2_CUBEMAP_NEGATIVEZ & caps2) {
//qDebug() << "DDS found -z";
++faces;
}

if (6 != faces) {
qDebug() << "Unsupported DDS file, expected six faces";
return nullptr;
}

if (1 != arraySize) {
qDebug() << "Unsupported DDS file, expected one layer";
return nullptr;
}

if (DXGI_FORMAT_R16G16B16A16_FLOAT != dxgiFormat) {
qDebug() << "Unsupported DDS file, expected dxgi format: DXGI_FORMAT_R16G16B16A16_FLOAT";
return nullptr;
}
int components = 8;
int oneFaceSize = 0;
auto calculateOneFaceSizeAtLevel = [=](int level) {
return qMax(width >> level, 1) * qMax(height >> level, 1) * components;
};
for (quint32 level = 0; level < mipMapCount; ++level) {
oneFaceSize += calculateOneFaceSizeAtLevel(level);
}
int totalSize = arraySize * faces * oneFaceSize;
const QByteArray data = file.read(totalSize);
if (data.size() < totalSize) {
qDebug() << "DDS file invalid, expected total size:" << totalSize << "read size:" << data.size();
return nullptr;
}

int depth = 1;

auto textures = std::make_unique<std::vector<std::unique_ptr<QOpenGLTexture>>>();
textures->resize(6);

uint64_t dataOffset = 0;
for (quint32 layer = 0; layer < arraySize; ++layer) {
for (quint32 face = 0; face < faces; ++face) {
for (quint32 level = 0; level < mipMapCount; ++level) {
if (0 == layer && 0 == level) {
QOpenGLTexture *texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
texture->setMinificationFilter(QOpenGLTexture::NearestMipMapNearest);
texture->setMagnificationFilter(QOpenGLTexture::Nearest);
texture->setFormat(QOpenGLTexture::RGBA16F);
texture->setSize(width, height, depth);
texture->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::Float16);
texture->setData(QOpenGLTexture::RGBA, QOpenGLTexture::Float16, data.constData() + dataOffset);
textures->at(face).reset(texture);
}
dataOffset += calculateOneFaceSizeAtLevel(level);
}
}
}
return std::move(textures);
}

QOpenGLTexture *DdsFileReader::createOpenGLTexture()
{
QFile file(m_filename);
Expand Down Expand Up @@ -423,7 +556,7 @@ QOpenGLTexture *DdsFileReader::createOpenGLTexture()
//qDebug() << "DDS resource dimension:" << ResourceDimensionToString((D3D10_RESOURCE_DIMENSION)qFromLittleEndian<quint32>(&fileHeader.header10.resourceDimension));
//qDebug() << "DDS misc flag:" << MiscFlagToString((UINT)qFromLittleEndian<quint32>(&fileHeader.header10.miscFlag));

auto faces = 0;
quint32 faces = 0;
if (DDSCAPS2_CUBEMAP_POSITIVEX & caps2) {
//qDebug() << "DDS found +x";
++faces;
Expand Down Expand Up @@ -468,7 +601,7 @@ QOpenGLTexture *DdsFileReader::createOpenGLTexture()
auto calculateOneFaceSizeAtLevel = [=](int level) {
return qMax(width >> level, 1) * qMax(height >> level, 1) * components;
};
for (auto level = 0; level < mipMapCount; ++level) {
for (quint32 level = 0; level < mipMapCount; ++level) {
oneFaceSize += calculateOneFaceSizeAtLevel(level);
}
int totalSize = arraySize * faces * oneFaceSize;
Expand Down Expand Up @@ -502,9 +635,9 @@ QOpenGLTexture *DdsFileReader::createOpenGLTexture()
}

uint64_t dataOffset = 0;
for (int layer = 0; layer < arraySize; ++layer) {
for (int face = 0; face < faces; ++face) {
for (int level = 0; level < mipMapCount; ++level) {
for (quint32 layer = 0; layer < arraySize; ++layer) {
for (quint32 face = 0; face < faces; ++face) {
for (quint32 level = 0; level < mipMapCount; ++level) {
QOpenGLPixelTransferOptions uploadOptions;
uploadOptions.setAlignment(1);
texture->setData(level,
Expand Down
2 changes: 2 additions & 0 deletions application/sources/dds_file.h
@@ -1,6 +1,7 @@
#ifndef DUST3D_APPLICATION_DDS_FILE_H_
#define DUST3D_APPLICATION_DDS_FILE_H_

#include <memory>
#include <QString>
#include <QOpenGLTexture>

Expand All @@ -9,6 +10,7 @@ class DdsFileReader
public:
DdsFileReader(const QString &filename);
QOpenGLTexture *createOpenGLTexture();
std::unique_ptr<std::vector<std::unique_ptr<QOpenGLTexture>>> createOpenGLTextures();
private:
QString m_filename;
};
Expand Down
11 changes: 8 additions & 3 deletions application/sources/model_opengl_program.cc
Expand Up @@ -23,6 +23,11 @@ void ModelOpenGLProgram::addShaderFromResource(QOpenGLShader::ShaderType type, c
dust3dDebug << "Failed to addShaderFromResource, resource:" << resourceName << ", " << log().toStdString();
}

bool ModelOpenGLProgram::isCoreProfile() const
{
return m_isCoreProfile;
}

void ModelOpenGLProgram::load(bool isCoreProfile)
{
if (m_isLoaded)
Expand All @@ -49,12 +54,12 @@ void ModelOpenGLProgram::load(bool isCoreProfile)
m_isLoaded = true;
}

int ModelOpenGLProgram::getUniformLocationByName(const char *name)
int ModelOpenGLProgram::getUniformLocationByName(const std::string &name)
{
auto findLocation = m_uniformLocationMap.find(name);
if (findLocation != m_uniformLocationMap.end())
return findLocation->second;
int location = uniformLocation(name);
m_uniformLocationMap.insert({std::string(name), location});
int location = uniformLocation(name.c_str());
m_uniformLocationMap.insert({name, location});
return location;
}

0 comments on commit d512ec0

Please sign in to comment.