Я уже довольно давно работаю над этим движком opengl и делаю на нем игру с процедурно сгенерированными планетами. Однако у меня возникли некоторые странные проблемы, с которыми я застрял в течение 3 недель.
Чтобы генерировать планеты на лету, я использую материал, состоящий из тесселяции и геометрического шейдера для генерации дюйм. Таким образом, это супер быстро. Теперь в шейдере оценки тесселяции я использую единый буферный блок для отправки параметров генерации pl anet в графический процессор (что, я считаю, составляет 48 байтов x количество слоев шума).
Теперь все это отлично работает на моем настольном ПК P C с radeon r9 390, а также на моем ноутбуке с gtx 1060. Однако на следующем графическом процессоре настольного компьютера я получаю INVALID_INDEX при вызове glGetUniformBlockIndex, и, очевидно, он не работает как пытается создать с неверными параметрами. - gtx 1050 - gtx 1060 - gtx 960 - gtx 970 - rtx 2080
На следующих графических процессорах все работает нормально без ошибок: - radeon r9 390 - rx 580 - hd 7770
I у меня нет других доступных графических процессоров для дальнейшего тестирования.
Теперь, после некоторого исследования, я знаю, что существует ограничение на то, насколько большим может быть ваш единый буферный блок, а также сколько и как многие компоненты могут быть у вас c. Однако, учитывая, что я уменьшил максимальное количество слоев до 1 и снова протестировал, проблема все еще сохранялась. Это также не может быть проблемой нехватки памяти, потому что у hd 7770 всего 1 ГБ vram, а у 1060 от 4-6 ГБ vram, и это все еще происходит на 1060.
Еще одна вещь, которую я нашел заключается в том, что переменные шейдера могут быть оптимизированы драйвером, когда переменная не влияет на вывод, и, как вы можете видеть из кода шейдера, она определенно вносит свой вклад в вывод.
Итак, на некоторый код
В классе материала есть функция CreateUniformBuffer, которая получает индекс блока унифицированного буфера и связывает его с буфером, позволяя редактировать его. Обратите внимание, что программа уже вылетает после получения INVALID_INDEX, поэтому проблема должна быть получена.
GLuint Material::CreateUniformBuffer(const std::string& name, GLuint bufferSize)
{
GLuint uniformBlockIndex = glGetUniformBlockIndex(m_pShader->m_ShaderProgramID, name.data());
Utilities::Debug::LogGLError(glGetError());
if (uniformBlockIndex == GL_INVALID_INDEX)
{
Utilities::Debug::LogError("Material::CreateUniformBuffer > Uniform buffer block with name " + name + " not found!");
return 0;
}
Utilities::Debug::LogGLError(glGetError());
glUniformBlockBinding(m_pShader->m_ShaderProgramID, uniformBlockIndex, m_BufferBindIndex);
Utilities::Debug::LogGLError(glGetError());
// Uniform buffer object for lights
GLuint bufferID;
glGenBuffers(1, &bufferID);
Utilities::Debug::LogGLError(glGetError());
glBindBuffer(GL_UNIFORM_BUFFER, bufferID);
Utilities::Debug::LogGLError(glGetError());
glBufferData(GL_UNIFORM_BUFFER, bufferSize, NULL, GL_DYNAMIC_DRAW);
Utilities::Debug::LogGLError(glGetError());
glBindBufferBase(GL_UNIFORM_BUFFER, uniformBlockIndex, bufferID);
Utilities::Debug::LogGLError(glGetError());
glBindBuffer(GL_UNIFORM_BUFFER, 0);
Utilities::Debug::LogGLError(glGetError());
m_UniformBufferObjects.push_back(bufferID);
++m_BufferBindIndex;
return bufferID;
}
Это шейдер оценки тесселяции, обратите внимание, что вверху есть строка #include "SimplexNoise" , который не работает в opengl, однако у движка есть этап прекомпиляции для шейдеров, на котором он считывает код шейдера и заменяет любые директивы #include содержимым файла, который он включает перед компиляцией шейдера.
planet_te.shader
#version 450
#include "SimplexNoise.shader"
layout(triangles, equal_spacing, cw) in;
in vec3 tcPosition[];
out vec3 tePosition;
out float teElevation;
uniform int NumNoiseLayers;
struct NoiseLayer
{
float Strength;
float BaseRoughness;
float Roughness;
float Persistance;
vec3 Center;
float MinValue;
int NumLayers;
int UseFirstLayerAsMask;
int NoiseFilterType;
float Weight;
};
const int MaxNoiseLayers = 4;
layout(std140) uniform NoiseBlock
{
NoiseLayer NoiseLayers[MaxNoiseLayers];
} _NoiseData;
float Evaluate(vec3 p, int layer)
{
int filterType = _NoiseData.NoiseLayers[layer].NoiseFilterType;
if (filterType == 0)
return SimpleEvaluate(p, int(_NoiseData.NoiseLayers[layer].NumLayers), _NoiseData.NoiseLayers[layer].BaseRoughness, _NoiseData.NoiseLayers[layer].Roughness, _NoiseData.NoiseLayers[layer].Persistance, _NoiseData.NoiseLayers[layer].Center, _NoiseData.NoiseLayers[layer].MinValue, _NoiseData.NoiseLayers[layer].Strength);
return RigidEvaluate(p, int(_NoiseData.NoiseLayers[layer].NumLayers), _NoiseData.NoiseLayers[layer].BaseRoughness, _NoiseData.NoiseLayers[layer].Roughness, _NoiseData.NoiseLayers[layer].Persistance, _NoiseData.NoiseLayers[layer].Center, _NoiseData.NoiseLayers[layer].MinValue, _NoiseData.NoiseLayers[layer].Strength, _NoiseData.NoiseLayers[layer].Weight);
}
float CalculateTotalStrength()
{
float strength = 0.0;
for (int i = 0; i < NumNoiseLayers; i++)
{
strength += _NoiseData.NoiseLayers[i].Strength;
}
return strength;
}
float LayeredEvaluate(vec3 p)
{
float firstLayerValue = 0.0;
float elevationAverage = 0.0;
float totalStrength = CalculateTotalStrength();
float unscaledElevation = 0.0;
float scaledElevation = 0.0;
float noiseValue = 0.0;
float strengthPercentage = 0.0;
if (NumNoiseLayers > 0)
{
unscaledElevation = Evaluate(p, 0);
scaledElevation = max(0.0, unscaledElevation);
noiseValue = scaledElevation;
elevationAverage = unscaledElevation;
firstLayerValue = noiseValue;
}
for (int i = 1; i < NumNoiseLayers; i++)
{
float mask = (_NoiseData.NoiseLayers[i].UseFirstLayerAsMask == 1) ? firstLayerValue : 1.0;
unscaledElevation = Evaluate(p, 0);
scaledElevation = max(0.0, unscaledElevation);
elevationAverage += unscaledElevation;
noiseValue += scaledElevation;
}
elevationAverage /= totalStrength;
teElevation = clamp(elevationAverage * 115.0, -0.99, 0.99);
return noiseValue;
}
void main()
{
vec3 p0 = gl_TessCoord.x * tcPosition[0];
vec3 p1 = gl_TessCoord.y * tcPosition[1];
vec3 p2 = gl_TessCoord.z * tcPosition[2];
tePosition = normalize(p0 + p1 + p2);
float hieght = LayeredEvaluate(tePosition);
gl_Position = vec4(tePosition * (1.0 + hieght), 1);
}
SimplexNoise.shader
const int RandomSize = 256;
const float Sqrt3 = 1.7320508075688772935;
const float Sqrt5 = 2.2360679774997896964;
uniform int _random[512];
/// Skewing and unskewing factors for 2D, 3D and 4D,
/// some of them pre-multiplied.
const float F2 = 0.5 * (Sqrt3 - 1.0);
const float G2 = (3.0 - Sqrt3) / 6.0;
const float G22 = G2 * 2.0 - 1;
const float F3 = 1.0 / 3.0;
const float G3 = 1.0 / 6.0;
const float F4 = (Sqrt5 - 1.0) / 4.0;
const float G4 = (5.0 - Sqrt5) / 20.0;
const float G42 = G4 * 2.0;
const float G43 = G4 * 3.0;
const float G44 = G4 * 4.0 - 1.0;
const int[] Grad3 =
{
1, 1, 0, -1, 1, 0, 1, -1, 0,
-1, -1, 0, 1, 0, 1, -1, 0, 1,
1, 0, -1, -1, 0, -1, 0, 1, 1,
0, -1, 1, 0, 1, -1, 0, -1, -1
};
float Dot(int index, float x, float y, float z)
{
return Grad3[index] * x + Grad3[index + 1] * y + Grad3[index + 2] * z;
}
float Dot(int index, float x, float y)
{
return Grad3[index] * x + Grad3[index + 1] * y;
}
int FastFloor(float x)
{
return int(x) >= 0 ? int(x) : int(x) - 1;
}
float Evaluate(vec3 p)
{
float x = p.x;
float y = p.y;
float z = p.z;
float n0 = 0.0, n1 = 0.0, n2 = 0.0, n3 = 0.0;
// Noise contributions from the four corners
// Skew the input space to determine which simplex cell we're in
float s = (x + y + z) * F3;
// for 3D
int i = FastFloor(x + s);
int j = FastFloor(y + s);
int k = FastFloor(z + s);
float t = (i + j + k) * G3;
// The x,y,z distances from the cell origin
float x0 = x - (i - t);
float y0 = y - (j - t);
float z0 = z - (k - t);
// For the 3D case, the simplex shape is a slightly irregular tetrahedron.
// Determine which simplex we are in.
// Offsets for second corner of simplex in (i,j,k)
int i1, j1, k1;
// coords
int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
if (x0 >= y0)
{
if (y0 >= z0)
{
// X Y Z order
i1 = 1;
j1 = 0;
k1 = 0;
i2 = 1;
j2 = 1;
k2 = 0;
}
else if (x0 >= z0)
{
// X Z Y order
i1 = 1;
j1 = 0;
k1 = 0;
i2 = 1;
j2 = 0;
k2 = 1;
}
else
{
// Z X Y order
i1 = 0;
j1 = 0;
k1 = 1;
i2 = 1;
j2 = 0;
k2 = 1;
}
}
else
{
// x0 < y0
if (y0 < z0)
{
// Z Y X order
i1 = 0;
j1 = 0;
k1 = 1;
i2 = 0;
j2 = 1;
k2 = 1;
}
else if (x0 < z0)
{
// Y Z X order
i1 = 0;
j1 = 1;
k1 = 0;
i2 = 0;
j2 = 1;
k2 = 1;
}
else
{
// Y X Z order
i1 = 0;
j1 = 1;
k1 = 0;
i2 = 1;
j2 = 1;
k2 = 0;
}
}
// A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
// a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z),
// and
// a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z),
// where c = 1/6.
// Offsets for second corner in (x,y,z) coords
float x1 = x0 - i1 + G3;
float y1 = y0 - j1 + G3;
float z1 = z0 - k1 + G3;
// Offsets for third corner in (x,y,z)
float x2 = x0 - i2 + F3;
float y2 = y0 - j2 + F3;
float z2 = z0 - k2 + F3;
// Offsets for last corner in (x,y,z)
float x3 = x0 - 0.5;
float y3 = y0 - 0.5;
float z3 = z0 - 0.5;
// Work out the hashed gradient indices of the four simplex corners
int ii = i & 0xff;
int jj = j & 0xff;
int kk = k & 0xff;
// Calculate the contribution from the four corners
float t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;
if (t0 > 0)
{
t0 *= t0;
int gi0 = _random[ii + _random[jj + _random[kk]]] % 12;
n0 = t0 * t0 * Dot(gi0 * 3, x0, y0, z0);
}
float t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;
if (t1 > 0)
{
t1 *= t1;
int gi1 = _random[ii + i1 + _random[jj + j1 + _random[kk + k1]]] % 12;
n1 = t1 * t1 * Dot(gi1 * 3, x1, y1, z1);
}
float t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;
if (t2 > 0)
{
t2 *= t2;
int gi2 = _random[ii + i2 + _random[jj + j2 + _random[kk + k2]]] % 12;
n2 = t2 * t2 * Dot(gi2 * 3, x2, y2, z2);
}
float t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;
if (t3 > 0)
{
t3 *= t3;
int gi3 = _random[ii + 1 + _random[jj + 1 + _random[kk + 1]]] % 12;
n3 = t3 * t3 * Dot(gi3 * 3, x3, y3, z3);
}
// Add contributions from each corner to get the final noise value.
// The result is scaled to stay just inside [-1,1]
return float(n0 + n1 + n2 + n3) * 32;
}
float Evaluate(vec3 p, float strength, float roughness, vec3 centre)
{
float noise = (Evaluate(p * roughness + centre) + 1.0) * 0.5;
return noise * strength;
}
float SimpleEvaluate(vec3 p, int numLayers, float baseRoughness, float roughness, float persistance, vec3 centre, float minValue, float strength)
{
float noiseValue = 0.0;
float frequency = baseRoughness;
float amplitude = 1.0;
for (int i = 0; i < numLayers; i++)
{
float v = Evaluate(p * frequency + centre);
noiseValue += (v + 1) * 0.5 * amplitude;
frequency *= roughness;
amplitude *= persistance;
}
//noiseValue = max(0.0, noiseValue - minValue);
return (noiseValue - minValue) * strength;
}
float RigidEvaluate(vec3 p, int numLayers, float baseRoughness, float roughness, float persistance, vec3 centre, float minValue, float strength, float weight)
{
float noiseValue = 0.0;
float frequency = baseRoughness;
float amplitude = 1.0;
weight = 1.0;
for (int i = 0; i < numLayers; i++)
{
float v = 1.0 - abs(Evaluate(p * frequency + centre));
v *= v;
v *= weight;
weight = v;
noiseValue += v * amplitude;
frequency *= roughness;
amplitude *= persistance;
}
//noiseValue = max(0.0, noiseValue - minValue);
return (noiseValue - minValue) * strength;
}
Обратите внимание, что это алгоритм шума, который я нашел в Интернете и преобразовал в код glsl (привет Себастьяну Лагу за его потрясающую серию на процедурных планетах в Unity)
Я использую SDL для открытия окна и обработки ввода, аудио и рендеринга текста, OpenGL 4.6 и GLEW 2.1.0
Вещи, которые я пробовал: - Ниже размер блока за счет уменьшения количества слоев (поэтому меньший массив) - однородный массив вместо un блок iform (тот же результат, только cra sh отсутствует, но для графических процессоров с cra sh он просто неправильно отображает все планеты) - Обновление SDL и всех его плагинов до последней версии и обновление OpenGL до 4.6 с 3.1, также обновляя GLEW с 1.10.0 до 2.1.0 - Изменение имени унифицированного блока - Игра с количеством значений внутри блока (при соблюдении размера, который должен быть умножением на 4 числа с плавающей запятой) - Привязка = 0 рядом с std140 в коде glsl - ведение журнала любых ошибок или предупреждений от компиляции шейдера в консоль (ошибок или предупреждений не обнаружено)
У меня немного крайний срок в следующую пятницу для загрузки сборки в Steam, и они не будут исключать его, если он работает только на amd gpu (понятно, конечно), поэтому я надеюсь, что кто-то имеет представление о том, что я делаю здесь не так, или у кого-то была аналогичная проблема раньше в прошлом, любая помощь добро пожаловать!
РЕДАКТИРОВАТЬ: Я пробовал использовать SSBO, это оказало сильное sh влияние на производительность, и хотя sh больше не работает (потому что я не ищу индекс блока по имени), он не отображает что-нибудь на nvidea gpu и выплюнуть Неизвестные ошибки в файл журнала.
Код для привязки SSBO
GLuint Material::CreateShaderStorageBuffer(const std::string& name, GLsizeiptr bufferSize, const void* data, GLint bindingIndex, GLenum usage)
{
GLuint ssbo;
glGenBuffers(1, &ssbo);
Utilities::Debug::LogGLError(glGetError());
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
Utilities::Debug::LogGLError(glGetError());
glBufferData(GL_SHADER_STORAGE_BUFFER, bufferSize, data, usage);
Utilities::Debug::LogGLError(glGetError());
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, bindingIndex, ssbo);
Utilities::Debug::LogGLError(glGetError());
/*GLuint blockIndex = glGetProgramResourceIndex(m_pShader->m_ShaderProgramID, GL_SHADER_STORAGE_BLOCK, name.c_str());
Utilities::Debug::LogGLError(glGetError());
if (blockIndex == GL_INVALID_INDEX)
{
Utilities::Debug::LogError("Material::CreateShaderStorageBuffer > Shader Storage Buffer Block with name " + name + " not found!");
return 0;
}
glShaderStorageBlockBinding(m_pShader->m_ShaderProgramID, blockIndex, bindingIndex);
Utilities::Debug::LogGLError(glGetError());*/
glBindBuffer(GL_SHADER_STORAGE_BUFFER, NULL);
Utilities::Debug::LogGLError(glGetError());
return ssbo;
}
Код для установки данных SSBO
void Material::WriteToShaderStorageBuffer(GLuint ssboID, const void* data, GLsizeiptr size)
{
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssboID);
Utilities::Debug::LogGLError(glGetError());
GLvoid* bufferData = glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_WRITE_ONLY);
Utilities::Debug::LogGLError(glGetError());
memcpy(bufferData, data, size);
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
Utilities::Debug::LogGLError(glGetError());
glBindBuffer(GL_SHADER_STORAGE_BUFFER, NULL);
Utilities::Debug::LogGLError(glGetError());
}
Изменение в шейдере, чтобы превратить единый блок в SSBO
const int MaxNoiseLayers = 4;
layout(std430, binding = 0) buffer NoiseBlock
{
NoiseLayer NoiseLayers[MaxNoiseLayers];
} _NoiseData;