pass texture xattr 'shader-shark.texture.scale' to the shaders as 'uTextureScale' + update variable locations on each shader reload v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Tue, 26 Dec 2023 23:46:45 +0100
branchv_0
changeset 28 4cbd9c0beb4c
parent 27 2a156cb51479
child 29 69903e30b00d
pass texture xattr 'shader-shark.texture.scale' to the shaders as 'uTextureScale' + update variable locations on each shader reload
Shark.cpp
Texture.cpp
Texture.h
shaders/default.frag
shaders/phosphor-basic.frag
shaders/scanlines-basic.frag
shaders/soften-borders.frag
--- a/Shark.cpp	Tue Dec 26 15:41:07 2023 +0100
+++ b/Shark.cpp	Tue Dec 26 23:46:45 2023 +0100
@@ -18,6 +18,7 @@
 #include <iostream>
 #include <iomanip>
 #include <string>
+#include <charconv>
 #include <memory>
 #include <functional>
 #include <sstream>
@@ -50,6 +51,7 @@
 		GLint uView = -2;
 		GLint uProjection = -2;
 		GLint uTexture = -2;
+		GLint uTextureScale = -2;
 	} ProgAttr;
 
 	struct {
@@ -185,7 +187,8 @@
 	void parametrizeTexture(std::shared_ptr<Texture> tex);
 	bool reloadTexture(const std::string& fileName);
 	void loadTextures();
-	std::shared_ptr<Program> loadShaders();
+	void loadShaders();
+	void updateVariableLocations();
 	bool reloadShader(const std::string& fileName);
 	void setTitle(const std::string& suffix = "");
 	static const std::string getDefaultFile(const std::string& relativePath);
@@ -269,7 +272,7 @@
 
 
 	// Load GLSL shaders:
-	shaderProgram = loadShaders();
+	loadShaders();
 	loadTextures();
 	loadVertices();
 
@@ -468,6 +471,9 @@
 	glm::mat4 model = glm::mat4(1.0f); // identity matrix
 	glUniformMatrix4fv(ProgAttr.uModel, 1, GL_FALSE, &model[0][0]);
 
+	// TODO: draw a rectangle for each texture
+	glUniform1f(ProgAttr.uTextureScale, textures[0]->getScale());
+
 	glDrawArrays(GL_TRIANGLES, 0, 2 * 3); // see loadVertices()
 	std::cerr << "GLSL: glDrawArrays()" << std::endl;
 }
@@ -546,8 +552,7 @@
 	XAttrs xa(tex->getFileName());
 	std::string magf = xa["shader-shark.texture.mag-filter"];
 	std::string minf = xa["shader-shark.texture.min-filter"];
-	// TODO: std::string scale = xa["shader-shark.texture.scale"];
-	// TODO: keep MappedFile locked until we read the attributes
+	std::string scale = xa["shader-shark.texture.scale"];
 
 	auto GLT2D = GL_TEXTURE_2D;
 	auto MAG = GL_TEXTURE_MAG_FILTER;
@@ -558,6 +563,14 @@
 
 	if (minf == "linear") glTexParameteri(GLT2D, MIN, GL_LINEAR);
 	else if (minf == "nearest") glTexParameteri(GLT2D, MIN, GL_NEAREST);
+
+	if (scale.size()) {
+		float sc;
+		if (std::from_chars(scale.data(), scale.data() + scale.size(), sc).ec
+				== std::errc{}) tex->setScale(sc);
+		else std::cerr << "Invalid texture scale value - expecting float\n";
+		// tex->setScale(std::stof(scale)); // locale-dependent (. vs ,)
+	}
 }
 
 void Shark::Impl::loadTextures() {
@@ -595,82 +608,77 @@
 	return false;
 }
 
-std::shared_ptr<Program> Shark::Impl::loadShaders() {
-	try {
-		// Vertex Array Object (VAO)
-		GLuint vao;
-		glGenVertexArrays(1, &vao);
-		glBindVertexArray(vao);
-		// VAO - something like context for bound data/variables
-		// We can switch multiple VAOs. VAO can contain multiple VBOs.
-		// what-are-vertex-array-objects
+void Shark::Impl::loadShaders() {
+	// Vertex Array Object (VAO)
+	GLuint vao;
+	glGenVertexArrays(1, &vao);
+	glBindVertexArray(vao);
+	// VAO - something like context for bound data/variables
+	// We can switch multiple VAOs. VAO can contain multiple VBOs.
+	// what-are-vertex-array-objects
 
-		// Vertex Buffer Object (VBO):
-		GLuint vbo;
-		glGenBuffers(1, &vbo);
-		glBindBuffer(GL_ARRAY_BUFFER, vbo);
+	// Vertex Buffer Object (VBO):
+	GLuint vbo;
+	glGenBuffers(1, &vbo);
+	glBindBuffer(GL_ARRAY_BUFFER, vbo);
 
-		{
-			// Load default shaders if there are no configured:
-			int vc = 0;
-			int fc = 0;
-			auto& ss = cfg.shaders;
-			for (const auto& s : ss) if (s.type == "vertex") vc++;
-			for (const auto& s : ss) if (s.type == "fragment") fc++;
-			auto& d = getDefaultFile;
-			if (vc == 0) ss.push_back({d("shaders/default.vert"), "vertex"});
-			if (fc == 0) ss.push_back({d("shaders/default.frag"), "fragment"});
-		}
+	{
+		// Load default shaders if there are no configured:
+		int vc = 0;
+		int fc = 0;
+		auto& ss = cfg.shaders;
+		for (const auto& s : ss) if (s.type == "vertex") vc++;
+		for (const auto& s : ss) if (s.type == "fragment") fc++;
+		auto& d = getDefaultFile;
+		if (vc == 0) ss.push_back({d("shaders/default.vert"), "vertex"});
+		if (fc == 0) ss.push_back({d("shaders/default.frag"), "fragment"});
+	}
 
-		std::shared_ptr<Program> program = std::make_shared<Program>();
+	shaderProgram = std::make_shared<Program>();
 
-		// glBindFragDataLocation(program, 0, "outColor");
-		// glBindAttribLocation(program, LOC.input, "vertices");
+	// glBindFragDataLocation(program, 0, "outColor");
+	// glBindAttribLocation(program, LOC.input, "vertices");
 
-		for (const Configuration::Shader definition : cfg.shaders) {
-			Shader::Type type;
-			std::string fileName = definition.fileName;
-			if (definition.type == "fragment") type = Shader::Type::FRAGMENT;
-			else if (definition.type == "vertex") type = Shader::Type::VERTEX;
-			else throw std::invalid_argument("unsupported shader type");
+	for (const Configuration::Shader definition : cfg.shaders) {
+		Shader::Type type;
+		std::string fileName = definition.fileName;
+		if (definition.type == "fragment") type = Shader::Type::FRAGMENT;
+		else if (definition.type == "vertex") type = Shader::Type::VERTEX;
+		else throw std::invalid_argument("unsupported shader type");
 
-			MappedFile file(fileName);
-			std::shared_ptr<Shader> shader = std::make_shared<Shader>(
-					type, file, fileName);
+		MappedFile file(fileName);
+		std::shared_ptr<Shader> shader = std::make_shared<Shader>(
+				type, file, fileName);
 
-			program->attachShader(*shader.get());
-			shaders.push_back(shader);
-			watchedFiles.push_back(fileMonitor.watch(fileName));
-			std::cerr << "GLSL loaded: " << fileName.c_str() << std::endl;
-			// We may detach and delete shaders,
-			// but our shaders are small, so we keep them for later reloading.
-		}
+		shaderProgram->attachShader(*shader.get());
+		shaders.push_back(shader);
+		watchedFiles.push_back(fileMonitor.watch(fileName));
+		std::cerr << "GLSL loaded: " << fileName.c_str() << std::endl;
+		// We may detach and delete shaders,
+		// but our shaders are small, so we keep them for later reloading.
+	}
 
-		// GLSL compiler does very efficient / aggressive optimization.
-		// Attributes and uniforms that are not used in the shader are deleted.
-		// And even if we e.g. read color from a texture and overwrite it,
-		// the variable is still deleted and considered „inactive“.
-		// Functions glGetAttribLocation() and glGetUniformLocation() return -1.
-
-		program->link();
+	shaderProgram->link();
+	updateVariableLocations();
+	// listVariables(program);
+	std::cerr << "GLSL shader count: " << shaders.size() << std::endl;
+}
 
-		ProgAttr.aVertexXYZ = program->getAttribLocation("aVertexXYZ");
-		ProgAttr.aTextureXY = program->getAttribLocation("aTextureXY");
-		ProgAttr.uModel = program->getUniformLocation("uModel");
-		ProgAttr.uView = program->getUniformLocation("uView");
-		ProgAttr.uProjection = program->getUniformLocation("uProjection");
-		ProgAttr.uTexture = program->getUniformLocation("uTexture");
-		ProgAttr.fColor = program->getFragDataLocation("fColor");
-		program->bindFragDataLocation("fColor", ProgAttr.fColor);
-		// listVariables(program);
-		std::cerr << "GLSL shader count: " << shaders.size() << std::endl;
-		return program;
-	} catch (const std::exception& e) {
-		std::cerr << "Error while loading shaders: " << e.what() << std::endl;
-	} catch (...) {
-		std::cerr << "Error while loading shaders: unknown" << std::endl;
-	}
-	throw std::logic_error("GLSL: loadShaders() failed");
+void Shark::Impl::updateVariableLocations() {
+	// GLSL compiler does very efficient / aggressive optimization.
+	// Attributes and uniforms that are not used in the shader are deleted.
+	// And even if we e.g. read color from a texture and overwrite it,
+	// the variable is still deleted and considered „inactive“.
+	// Functions glGetAttribLocation() and glGetUniformLocation() return -1.
+	ProgAttr.aVertexXYZ = shaderProgram->getAttribLocation("aVertexXYZ");
+	ProgAttr.aTextureXY = shaderProgram->getAttribLocation("aTextureXY");
+	ProgAttr.uModel = shaderProgram->getUniformLocation("uModel");
+	ProgAttr.uView = shaderProgram->getUniformLocation("uView");
+	ProgAttr.uProjection = shaderProgram->getUniformLocation("uProjection");
+	ProgAttr.uTexture = shaderProgram->getUniformLocation("uTexture");
+	ProgAttr.uTextureScale = shaderProgram->getUniformLocation("uTextureScale");
+	ProgAttr.fColor = shaderProgram->getFragDataLocation("fColor");
+	shaderProgram->bindFragDataLocation("fColor", ProgAttr.fColor);
 }
 
 bool Shark::Impl::reloadShader(const std::string& fileName) {
@@ -678,6 +686,7 @@
 		if (shader->getFileName() == fileName) {
 			shader->update(MappedFile(fileName));
 			shaderProgram->link();
+			updateVariableLocations();
 			return true;
 		}
 	}
--- a/Texture.cpp	Tue Dec 26 15:41:07 2023 +0100
+++ b/Texture.cpp	Tue Dec 26 23:46:45 2023 +0100
@@ -27,6 +27,7 @@
 	std::string fileName;
 	int width;
 	int height;
+	GLfloat scale = 1.;
 };
 
 Texture::Texture(
@@ -64,6 +65,14 @@
 	return (GLfloat) impl->width / (GLfloat) impl->height;
 }
 
+GLfloat Texture::getScale() const {
+	return impl->scale;
+}
+
+void Texture::setScale(GLfloat scale) {
+	impl->scale = scale;
+}
+
 void Texture::update(int width, int height, const Buffer& img) {
 	impl->width = width;
 	impl->height = height;
--- a/Texture.h	Tue Dec 26 15:41:07 2023 +0100
+++ b/Texture.h	Tue Dec 26 23:46:45 2023 +0100
@@ -38,6 +38,8 @@
 	int getWidth() const;
 	int getHeight() const;
 	GLfloat getRatio() const;
+	GLfloat getScale() const;
+	void setScale(GLfloat scale);
 	void update(int width, int height, const Buffer& img);
 private:
 	class Impl;
--- a/shaders/default.frag	Tue Dec 26 15:41:07 2023 +0100
+++ b/shaders/default.frag	Tue Dec 26 23:46:45 2023 +0100
@@ -1,6 +1,7 @@
 #version 330 core
 
 uniform  sampler2D  uTexture;
+uniform  float      uTextureScale;
 in       vec2       vTextureXY;
 out      vec4       fColor;
 
--- a/shaders/phosphor-basic.frag	Tue Dec 26 15:41:07 2023 +0100
+++ b/shaders/phosphor-basic.frag	Tue Dec 26 23:46:45 2023 +0100
@@ -1,6 +1,7 @@
 #version 330 core
 
 uniform  sampler2D  uTexture;
+uniform  float      uTextureScale;
 in       vec2       vTextureXY;
 out      vec4       fColor;
 
@@ -26,10 +27,10 @@
 	// fColor = invert(fColor);
 	fColor = (fColor-rand(vTextureXY)*0.3) * 1.6;
 
-	float lineCount = textureSize(uTexture, 0).y;
+	float lineCount = textureSize(uTexture, 0).y / uTextureScale;
 	fColor.xyz *= abs(sin(radians(vTextureXY.y * 180. * lineCount)));
 
-	// float columnCount = textureSize(uTexture, 0).x;
+	// float columnCount = textureSize(uTexture, 0).x / uTextureScale;
 	// fColor.xyz *=
 	//  clamp(abs(sin(radians(vTextureXY.x * 180. * columnCount)))+0.6, 0., 1.);
 
--- a/shaders/scanlines-basic.frag	Tue Dec 26 15:41:07 2023 +0100
+++ b/shaders/scanlines-basic.frag	Tue Dec 26 23:46:45 2023 +0100
@@ -1,12 +1,13 @@
 #version 330 core
 
 uniform  sampler2D  uTexture;
+uniform  float      uTextureScale;
 in       vec2       vTextureXY;
 out      vec4       fColor;
 
 void main() {
 	fColor = texture(uTexture, vTextureXY).rgba;
 
-	float lineCount = textureSize(uTexture, 0).y;
+	float lineCount = textureSize(uTexture, 0).y / uTextureScale;
 	fColor.xyz *= abs(sin(radians(vTextureXY.y * 180. * lineCount)));
 }
--- a/shaders/soften-borders.frag	Tue Dec 26 15:41:07 2023 +0100
+++ b/shaders/soften-borders.frag	Tue Dec 26 23:46:45 2023 +0100
@@ -1,6 +1,7 @@
 #version 330 core
 
 uniform  sampler2D  uTexture;
+uniform  float      uTextureScale;
 in       vec2       vTextureXY;
 out      vec4       fColor;