Created
November 27, 2018 16:04
-
-
Save tyhenry/94638b597d17455db7c6254f5ad5d0f5 to your computer and use it in GitHub Desktop.
LUT shader - oF
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "ofMain.h" | |
#include "TextureFilter.h" | |
namespace TextureFilter { | |
class Lut3DFilter : public TextureFilterBase { | |
public: | |
Lut3DFilter() {} | |
~Lut3DFilter() {} | |
bool setup(string cubeFile, int lutSize = 64) { | |
// Load the LUT | |
vector<RGB> LUT; | |
LUTpath = ofToDataPath(cubeFile, true); | |
LUTsize = lutSize; | |
ifstream LUTfile(LUTpath.c_str()); | |
// read from file | |
while (!LUTfile.eof()) { | |
string LUTline; | |
getline(LUTfile, LUTline); | |
if (LUTline.empty()) continue; | |
RGB line; | |
if (sscanf(LUTline.c_str(), "%f %f %f", &line.r, &line.g, &line.b) == 3) LUT.push_back(line); | |
} | |
if (LUT.size() != (pow(LUTsize, 3.0))) { | |
LUT.clear(); | |
ofLogError("Lut3DFilter") << "Error loading CUBE file: " << LUTpath << " - LUT size is incorrect."; | |
return false; | |
} | |
// create 3d texture | |
// Reference from http://content.gpwiki.org/index.php/OpenGL:Tutorials:3D_Textures | |
glEnable(GL_TEXTURE_3D); | |
glGenTextures(1, &texture3D); | |
glBindTexture(GL_TEXTURE_3D, texture3D); | |
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT); | |
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT); | |
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT); | |
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB, LUTsize, LUTsize, LUTsize, 0, GL_RGB, | |
GL_FLOAT, &LUT[0]); | |
glBindTexture(GL_TEXTURE_3D, 0); | |
glDisable(GL_TEXTURE_3D); | |
ofLogNotice("Lut3DFilter") << "Loaded " << LUTpath << " to 3D texture with size " << LUTsize; | |
return linkShader(); | |
} | |
private: | |
// LUT | |
GLuint texture3D; | |
int LUTsize = 64; | |
string LUTpath; | |
struct RGB { float r, g, b; }; | |
bool filterSrcToDst(ofFbo* src, ofFbo* dst) override | |
{ | |
if (!src || !dst) return false; | |
dst->begin(); | |
ofClear(0); | |
shader.begin(); | |
ofTexture& tex0 = src->getTexture(); | |
shader.setUniformTexture("u_tex0", tex0, 0); | |
shader.setUniformTexture("lutTexture", GL_TEXTURE_3D, texture3D, 1); | |
shader.setUniform1f("lutSize", LUTsize); | |
plane.draw(); | |
shader.end(); | |
dst->end(); | |
return true; | |
} | |
string fragmentShader() override | |
{ | |
return versionSrc + fragShaderSrc; | |
} | |
// LUT 3D FILTER SHADER SOURCE | |
// fragment shader | |
const string fragShaderSrc = STRINGIFY( | |
uniform sampler2D u_tex0; | |
uniform sampler3D lutTexture; | |
varying vec2 texCoordVarying; | |
uniform float lutSize; | |
void main() { | |
// Based on "GPU Gems 2 � Chapter 24. Using Lookup Tables to Accelerate Color Transformations"; | |
// More info and credits @ http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter24.html | |
vec4 rawColor = texture2D(u_tex0, texCoordVarying); | |
// Compute the 3D LUT lookup scale/offset factor; | |
vec3 scale = vec3((lutSize - 1.0) / lutSize); | |
vec3 offset = vec3(1.0 / (2.0 * lutSize)); | |
// ****** Apply 3D LUT color transform! **************; | |
// This is our dependent texture read; The 3D texture's; | |
// lookup coordinates are dependent on the; | |
// previous texture read's result; | |
vec3 color = texture3D(lutTexture, scale * rawColor.rgb + offset).rgb; | |
//vec3 color = vec3(texCoordVarying.x,texCoordVarying.y,0.); | |
gl_FragColor = vec4(color, rawColor.a); | |
} | |
); | |
}; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "TextureFilter.h" | |
namespace TextureFilter { | |
void TextureFilterBase::applyFilter(const ofTexture & input, ofFbo & output, int nPasses) | |
{ | |
allocatePingpongs(input); | |
setupPlane(input); | |
ofFbo* src = &pingpong[0]; | |
ofFbo* dst = &pingpong[1]; | |
// init src | |
src->begin(); | |
ofClear(0); | |
input.draw(0, 0, src->getWidth(), src->getHeight()); | |
src->end(); | |
// ping pong | |
for (int i = 0; i < nPasses; i++) { | |
if (!filterSrcToDst(src, dst)) { // do the shader | |
ofLogError("TextureFilter") << "unable to run filter at pass: " << i; | |
break; // break if error | |
} | |
if (i == nPasses - 1) break; // last pass don't flip flop | |
// flipflop | |
ofFbo* tmp = src; | |
src = dst; | |
dst = tmp; | |
} | |
// draw to output | |
allocateFbo(output, input); | |
output.begin(); | |
ofClear(0); | |
dst->draw(0, 0, output.getWidth(), output.getHeight()); | |
output.end(); | |
} | |
bool TextureFilterBase::allocatePingpongs(const ofTexture & input) | |
{ | |
if (!input.isAllocated()) return false; | |
bool didIt = false; | |
for (int i = 0; i < 2; i++) { | |
didIt = allocateFbo(pingpong[i], input); | |
} | |
return didIt; | |
} | |
// static function allocates an fbo with texture dims and GL_RGBA | |
// --------------------------------------------------------------------------- | |
bool TextureFilterBase::allocateFbo(ofFbo& fbo, const ofTexture& texture) { | |
if (!texture.isAllocated()) return false; | |
float w = texture.getWidth(); | |
float h = texture.getHeight(); | |
if (fbo.getWidth() != w || fbo.getHeight() != h || fbo.getTexture().getTextureData().glInternalFormat != GL_RGBA) { | |
fbo.allocate(w, h, GL_RGBA); | |
fbo.begin(); | |
ofClear(0); | |
fbo.end(); | |
return true; | |
} | |
return fbo.isAllocated(); | |
} | |
// set up plane dims + pos to match texture dims | |
// --------------------------------------------------------------------------- | |
bool TextureFilterBase::setupPlane(const ofTexture & input) | |
{ | |
if (!input.isAllocated()) return false; | |
float w = input.getWidth(); | |
float h = input.getHeight(); | |
if (plane.getWidth() != w || plane.getHeight() != h) { // avoid unnecessary mesh reconstruction | |
plane.set(w, h, 2, 2); // texture dims and 2x2 grid | |
} | |
plane.resetTransform(); | |
plane.setPosition(w * 0.5, h * 0.5, 0); // make sure centered on texture dims | |
return true; | |
} | |
// expands stringified source, making new lines after ';' and '{' and '}' | |
// --------------------------------------------------------------------------- | |
string TextureFilterBase::expandLines(const string& src) { | |
string s = src; | |
ofStringReplace(s, ";", ";\n"); | |
ofStringReplace(s, "{", "{\n"); | |
ofStringReplace(s, "}", "}\n"); | |
return s; | |
} | |
} // end TextureFilter namespace |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "ofMain.h" | |
#define STRINGIFY(A) #A | |
namespace TextureFilter{ | |
class TextureFilterBase { | |
public: | |
TextureFilterBase() {} | |
virtual ~TextureFilterBase() {} | |
void applyFilter(const ofTexture& input, ofFbo& output, int nPasses); // runs filterSrcToDst() for n passes | |
const ofTexture& applyFilter(const ofTexture& input, int nPasses) { // stores and returns result | |
applyFilter(input, result, nPasses); | |
return result.getTexture(); | |
} | |
const ofFbo& getResult() { return result; } | |
virtual void draw(const ofTexture& input, float x, float y, float w, float h, int nPasses) { | |
applyFilter(input, nPasses); | |
result.draw(x,y,w,h); | |
} | |
virtual void draw(const ofTexture& input, float x, float y, int nPasses) { | |
applyFilter(input, nPasses); | |
result.draw(x,y); | |
} | |
// links the shader sources | |
virtual bool linkShader() { | |
return | |
( | |
shader.setupShaderFromSource(GL_VERTEX_SHADER, expandLines(vertexShader())) && | |
shader.setupShaderFromSource(GL_FRAGMENT_SHADER, expandLines(fragmentShader())) && | |
shader.linkProgram() | |
); | |
} | |
ofShader shader; | |
protected: | |
ofFbo pingpong[2], result; | |
ofPlanePrimitive plane; | |
bool allocatePingpongs(const ofTexture& input); | |
bool static allocateFbo(ofFbo& fbo, const ofTexture& texture); | |
bool setupPlane(const ofTexture& input); | |
static string expandLines(const string& src); // expands STRINGIFY shader source, adding new lines | |
// mutable functions: | |
// runs the shader | |
virtual bool filterSrcToDst(ofFbo* src, ofFbo* dst) | |
{ | |
if (!src || !dst) return false; | |
dst->begin(); | |
ofClear(0); | |
shader.begin(); | |
ofTexture& tex0 = src->getTexture(); | |
shader.setUniformTexture("u_tex0", tex0, 0); | |
shader.setUniform2f("u_tex0Resolution", tex0.getWidth(), tex0.getHeight()); | |
plane.draw(); | |
shader.end(); | |
dst->end(); | |
return true; | |
} | |
// prepare shader code | |
virtual string vertexShader() { | |
return versionSrc + vertShaderSrc; | |
} | |
virtual string fragmentShader() { | |
return versionSrc + expandLines(fragShaderSrc); | |
} | |
// DEFAULT SHADER CODE | |
// GLSL version declaration | |
const string versionSrc = "#version 120\n"; | |
// vertex shader | |
const string vertShaderSrc = STRINGIFY( | |
varying vec2 texCoordVarying; | |
void main() { | |
texCoordVarying = gl_MultiTexCoord0.xy; | |
gl_Position = ftransform(); | |
} | |
); | |
// fragment shader | |
const string fragShaderSrc = STRINGIFY( | |
uniform sampler2D u_tex0; | |
uniform vec2 u_tex0Resolution; | |
varying vec2 texCoordVarying; | |
void main() { | |
gl_FragColor = texture2D(u_tex0, texCoordVarying); | |
} | |
); | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment