Hello,
For my game (a wolf3d-like raycaster) i have to send a texture array to a fragment shader (used for the rendering part), something that is not possible with the SFML class sf::Shader.
So I use OpenGL for that part, but it doesn't work :-/
On the shader side I always end up with an empty sampler2DArray (0 layers)
However it works with any other uniform type...
The code :
ShaderRenderSurface::ShaderRenderSurface(const sf::Vector2f &t_size)
{
if (!sf::Shader::isAvailable())
{
throw_shader_error("Shaders are not supported on this machine !");
}
m_drawTexture.create(t_size.x, t_size.y);
if (!m_renderShader.loadFromFile(c_glRenderShader, sf::Shader::Fragment))
{
throw_shader_error("Error loading shader '" + std::string(c_glRenderShader) + "' !");
}
m_drawSurface.setTexture(m_drawTexture);
glCheck(glGenTextures(1, &m_texArray));
sf::Shader::bind(&m_renderShader);
glCheck(glUniform1i(glGetUniformLocation(m_renderShader.getNativeHandle(), "u_textures"), 0)); // Texture unit 0
}
void ShaderRenderSurface::update(const sf::Time &)
{
updateUniforms();
}
void ShaderRenderSurface::setSize(const sf::Vector2f &t_size)
{
m_drawTexture.create(t_size.x, t_size.y);
}
void ShaderRenderSurface::loadTextureArray(const std::vector<sf::Image> &t_textures)
{
sf::Shader::bind(&m_renderShader);
Utility::createTextureArray(m_texArray, t_textures);
}
void ShaderRenderSurface::draw(sf::RenderTarget &t_target, sf::RenderStates t_states) const
{
t_states.shader = &m_renderShader;
t_target.draw(m_drawSurface, t_states);
}
void ShaderRenderSurface::updateUniforms()
{
m_renderShader.setUniform("u_resolution", sf::Vector2f(m_drawTexture.getSize()));
m_renderShader.setUniform("u_pos", info.pos);
m_renderShader.setUniform("u_dir", info.dir);
}
void createTextureArray(GLuint t_tex, const std::vector<sf::Image> &t_textures)
{
assert(!t_textures.empty());
auto size_x = std::max_element(t_textures.cbegin(), t_textures.cend(),
[](const sf::Image& a, const sf::Image& b)
{
return a.getSize().x < b.getSize().x;
})->getSize().x;
auto size_y = std::max_element(t_textures.cbegin(), t_textures.cend(),
[](const sf::Image& a, const sf::Image& b)
{
return a.getSize().y < b.getSize().y;
})->getSize().y;
glCheck(glActiveTexture(GL_TEXTURE0 + 0));
glCheck(glBindTexture(GL_TEXTURE_2D_ARRAY, t_tex));
glCheck(glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, size_x, size_y, t_textures.size()));
for (size_t i { 0 }; i < t_textures.size(); ++i)
{
glCheck(glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, t_textures[i].getSize().x, t_textures[i].getSize().y,
1, GL_RGBA, GL_UNSIGNED_BYTE, t_textures[i].getPixelsPtr()));
}
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glCheck(glTexParameterf(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_MIN_FILTER,GL_LINEAR));
glCheck(glTexParameterf(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_MAG_FILTER,GL_LINEAR));
glCheck(glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE));
glCheck(glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE));
glCheck(glBindTexture(GL_TEXTURE_2D_ARRAY, 0));
}
The test shader :
#version 330
uniform vec2 u_resolution;
uniform vec2 u_pos = vec2(7.5, 7.5);
uniform vec2 u_dir = vec2(0, 1);
uniform sampler2DArray u_textures;
void main()
{
gl_FragColor = texture(u_textures, vec3(gl_FragCoord.xy / u_resolution.xy, 1));
}
The screen is always black (texture size 1x1, 0 layers)
The github project page if needed :
https://github.com/Stellaris-code/Rayfun