191 lines
6.5 KiB
Objective-C
191 lines
6.5 KiB
Objective-C
#import "GBGLShader.h"
|
|
#import <OpenGL/gl3.h>
|
|
|
|
/*
|
|
Loosely based of https://www.raywenderlich.com/70208/opengl-es-pixel-shaders-tutorial
|
|
|
|
This code probably makes no sense after I upgraded it to OpenGL 3, since OpenGL makes aboslute no sense and has zero
|
|
helpful documentation.
|
|
*/
|
|
|
|
static NSString * const vertex_shader = @"\n\
|
|
#version 150 \n\
|
|
in vec4 aPosition;\n\
|
|
void main(void) {\n\
|
|
gl_Position = aPosition;\n\
|
|
}\n\
|
|
";
|
|
|
|
@implementation GBGLShader
|
|
{
|
|
GLuint resolution_uniform;
|
|
GLuint texture_uniform;
|
|
GLuint previous_texture_uniform;
|
|
GLuint frame_blending_mode_uniform;
|
|
|
|
GLuint position_attribute;
|
|
GLuint texture;
|
|
GLuint previous_texture;
|
|
GLuint program;
|
|
}
|
|
|
|
+ (NSString *) shaderSourceForName:(NSString *) name
|
|
{
|
|
return [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:name
|
|
ofType:@"fsh"
|
|
inDirectory:@"Shaders"]
|
|
encoding:NSUTF8StringEncoding
|
|
error:nil];
|
|
}
|
|
|
|
- (instancetype)initWithName:(NSString *) shaderName
|
|
{
|
|
self = [super init];
|
|
if (self) {
|
|
// Program
|
|
NSString *fragment_shader = [[self class] shaderSourceForName:@"MasterShader"];
|
|
fragment_shader = [fragment_shader stringByReplacingOccurrencesOfString:@"{filter}"
|
|
withString:[[self class] shaderSourceForName:shaderName]];
|
|
program = [[self class] programWithVertexShader:vertex_shader fragmentShader:fragment_shader];
|
|
// Attributes
|
|
position_attribute = glGetAttribLocation(program, "aPosition");
|
|
// Uniforms
|
|
resolution_uniform = glGetUniformLocation(program, "output_resolution");
|
|
|
|
glGenTextures(1, &texture);
|
|
glBindTexture(GL_TEXTURE_2D, texture);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
texture_uniform = glGetUniformLocation(program, "image");
|
|
|
|
glGenTextures(1, &previous_texture);
|
|
glBindTexture(GL_TEXTURE_2D, previous_texture);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
previous_texture_uniform = glGetUniformLocation(program, "previous_image");
|
|
|
|
frame_blending_mode_uniform = glGetUniformLocation(program, "frame_blending_mode");
|
|
|
|
// Configure OpenGL
|
|
[self configureOpenGL];
|
|
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void) renderBitmap: (void *)bitmap previous:(void*) previous sized:(NSSize)srcSize inSize:(NSSize)dstSize scale: (double) scale withBlendingMode:(GB_frame_blending_mode_t)blendingMode
|
|
{
|
|
glUseProgram(program);
|
|
glUniform2f(resolution_uniform, dstSize.width * scale, dstSize.height * scale);
|
|
glActiveTexture(GL_TEXTURE0);
|
|
glBindTexture(GL_TEXTURE_2D, texture);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, srcSize.width, srcSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
|
|
glUniform1i(texture_uniform, 0);
|
|
glUniform1i(frame_blending_mode_uniform, blendingMode);
|
|
if (blendingMode) {
|
|
glActiveTexture(GL_TEXTURE1);
|
|
glBindTexture(GL_TEXTURE_2D, previous_texture);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, srcSize.width, srcSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, previous);
|
|
glUniform1i(previous_texture_uniform, 1);
|
|
}
|
|
glBindFragDataLocation(program, 0, "frag_color");
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
}
|
|
|
|
- (void)configureOpenGL
|
|
{
|
|
// Program
|
|
|
|
glUseProgram(program);
|
|
|
|
GLuint vao;
|
|
glGenVertexArrays(1, &vao);
|
|
glBindVertexArray(vao);
|
|
|
|
GLuint vbo;
|
|
glGenBuffers(1, &vbo);
|
|
|
|
// Attributes
|
|
|
|
|
|
static GLfloat const quad[16] = {
|
|
-1.f, -1.f, 0, 1,
|
|
-1.f, +1.f, 0, 1,
|
|
+1.f, -1.f, 0, 1,
|
|
+1.f, +1.f, 0, 1,
|
|
};
|
|
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW);
|
|
glEnableVertexAttribArray(position_attribute);
|
|
glVertexAttribPointer(position_attribute, 4, GL_FLOAT, GL_FALSE, 0, 0);
|
|
}
|
|
|
|
+ (GLuint)programWithVertexShader:(NSString*)vsh fragmentShader:(NSString*)fsh
|
|
{
|
|
// Build shaders
|
|
GLuint vertex_shader = [self shaderWithContents:vsh type:GL_VERTEX_SHADER];
|
|
GLuint fragment_shader = [self shaderWithContents:fsh type:GL_FRAGMENT_SHADER];
|
|
// Create program
|
|
GLuint program = glCreateProgram();
|
|
// Attach shaders
|
|
glAttachShader(program, vertex_shader);
|
|
glAttachShader(program, fragment_shader);
|
|
// Link program
|
|
glLinkProgram(program);
|
|
// Check for errors
|
|
GLint status;
|
|
glGetProgramiv(program, GL_LINK_STATUS, &status);
|
|
if (status == GL_FALSE) {
|
|
GLchar messages[1024];
|
|
glGetProgramInfoLog(program, sizeof(messages), 0, &messages[0]);
|
|
NSLog(@"%@:- GLSL Program Error: %s", self, messages);
|
|
}
|
|
// Delete shaders
|
|
glDeleteShader(vertex_shader);
|
|
glDeleteShader(fragment_shader);
|
|
|
|
return program;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
glDeleteProgram(program);
|
|
glDeleteTextures(1, &texture);
|
|
glDeleteTextures(1, &previous_texture);
|
|
|
|
/* OpenGL is black magic. Closing one view causes others to be completely black unless we reload their shaders */
|
|
/* We're probably not freeing thing in the right place. */
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:@"GBFilterChanged" object:nil];
|
|
}
|
|
|
|
+ (GLuint)shaderWithContents:(NSString*)contents type:(GLenum)type
|
|
{
|
|
|
|
const GLchar* source = [contents UTF8String];
|
|
// Create the shader object
|
|
GLuint shader = glCreateShader(type);
|
|
// Load the shader source
|
|
glShaderSource(shader, 1, &source, 0);
|
|
// Compile the shader
|
|
glCompileShader(shader);
|
|
// Check for errors
|
|
GLint status = 0;
|
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
|
if (status == GL_FALSE) {
|
|
GLchar messages[1024];
|
|
glGetShaderInfoLog(shader, sizeof(messages), 0, &messages[0]);
|
|
NSLog(@"%@:- GLSL Shader Error: %s", self, messages);
|
|
}
|
|
return shader;
|
|
}
|
|
|
|
@end
|