commit 31af39b61b2dbd78b21682983bce3d2154852309 Author: Raimon Zamora Date: Thu Jun 27 08:51:19 2024 +0200 - First glorious commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f8906c5 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +shaders diff --git a/basic.fragment b/basic.fragment new file mode 100644 index 0000000..12ec020 --- /dev/null +++ b/basic.fragment @@ -0,0 +1,9 @@ +#version 330 core +out vec4 FragColor; + +in vec4 vertexColor; // the input variable from the vertex shader (same name and same type) + +void main() +{ + FragColor = vertexColor; +} \ No newline at end of file diff --git a/crt.fragment b/crt.fragment new file mode 100644 index 0000000..9c6ee47 --- /dev/null +++ b/crt.fragment @@ -0,0 +1,141 @@ +// +// PUBLIC DOMAIN CRT STYLED SCAN-LINE SHADER +// +// by Timothy Lottes +// +// This is more along the style of a really good CGA arcade monitor. +// With RGB inputs instead of NTSC. +// The shadow mask example has the mask rotated 90 degrees for less chromatic aberration. +// +// Left it unoptimized to show the theory behind the algorithm. +// +// It is an example what I personally would want as a display option for pixel art games. +// Please take and use, change, or whatever. +// + +varying vec4 v_color; +varying vec2 v_texCoord; + +uniform sampler2D tex0; + +// Hardness of scanline. +// -8.0 = soft +// -16.0 = medium +float hardScan=-8.0; + +// Hardness of pixels in scanline. +// -2.0 = soft +// -4.0 = hard +float hardPix=-2.0; + +// Display warp. +// 0.0 = none +// 1.0/8.0 = extreme +vec2 warp=vec2(1.0/32.0,1.0/24.0); + +// Amount of shadow mask. +float maskDark=1.0; +float maskLight=1.5; + +vec2 res = vec2(640.0,480.0); // /3.0 + +//------------------------------------------------------------------------ + +// sRGB to Linear. +// Assuing using sRGB typed textures this should not be needed. +float ToLinear1(float c){return(c<=0.04045)?c/12.92:pow((c+0.055)/1.055,2.4);} +vec3 ToLinear(vec3 c){return vec3(ToLinear1(c.r),ToLinear1(c.g),ToLinear1(c.b));} + +// Linear to sRGB. +// Assuing using sRGB typed textures this should not be needed. +float ToSrgb1(float c){return(c<0.0031308?c*12.92:1.055*pow(c,0.41666)-0.055);} +vec3 ToSrgb(vec3 c){return vec3(ToSrgb1(c.r),ToSrgb1(c.g),ToSrgb1(c.b));} + +// Nearest emulated sample given floating point position and texel offset. +// Also zero's off screen. +vec3 Fetch(vec2 pos,vec2 off){ + pos=floor(pos*res+off)/res; + if(max(abs(pos.x-0.5),abs(pos.y-0.5))>0.5)return vec3(0.0,0.0,0.0); + return ToLinear(texture2D(tex0,pos.xy,-16.0).rgb);} + +// Distance in emulated pixels to nearest texel. +vec2 Dist(vec2 pos){pos=pos*res;return -((pos-floor(pos))-vec2(0.5));} + +// 1D Gaussian. +float Gaus(float pos,float scale){return exp2(scale*pos*pos);} + +// 3-tap Gaussian filter along horz line. +vec3 Horz3(vec2 pos,float off){ + vec3 b=Fetch(pos,vec2(-1.0,off)); + vec3 c=Fetch(pos,vec2( 0.0,off)); + vec3 d=Fetch(pos,vec2( 1.0,off)); + float dst=Dist(pos).x; + // Convert distance to weight. + float scale=hardPix; + float wb=Gaus(dst-1.0,scale); + float wc=Gaus(dst+0.0,scale); + float wd=Gaus(dst+1.0,scale); + // Return filtered sample. + return (b*wb+c*wc+d*wd)/(wb+wc+wd);} + +// 5-tap Gaussian filter along horz line. +vec3 Horz5(vec2 pos,float off){ + vec3 a=Fetch(pos,vec2(-2.0,off)); + vec3 b=Fetch(pos,vec2(-1.0,off)); + vec3 c=Fetch(pos,vec2( 0.0,off)); + vec3 d=Fetch(pos,vec2( 1.0,off)); + vec3 e=Fetch(pos,vec2( 2.0,off)); + float dst=Dist(pos).x; + // Convert distance to weight. + float scale=hardPix; + float wa=Gaus(dst-2.0,scale); + float wb=Gaus(dst-1.0,scale); + float wc=Gaus(dst+0.0,scale); + float wd=Gaus(dst+1.0,scale); + float we=Gaus(dst+2.0,scale); + // Return filtered sample. + return (a*wa+b*wb+c*wc+d*wd+e*we)/(wa+wb+wc+wd+we);} + +// Return scanline weight. +float Scan(vec2 pos,float off){ + float dst=Dist(pos).y; + return Gaus(dst+off,hardScan);} + +// Allow nearest three lines to effect pixel. +vec3 Tri(vec2 pos){ + vec3 a=Horz3(pos,-1.0); + vec3 b=Horz5(pos, 0.0); + vec3 c=Horz3(pos, 1.0); + float wa=Scan(pos,-1.0); + float wb=Scan(pos, 0.0); + float wc=Scan(pos, 1.0); + return a*wa+b*wb+c*wc;} + +// Distortion of scanlines, and end of screen alpha. +vec2 Warp(vec2 pos){ + pos=pos*2.0-1.0; + pos*=vec2(1.0+(pos.y*pos.y)*warp.x,1.0+(pos.x*pos.x)*warp.y); + return pos*0.5+0.5;} + +// Shadow mask. +vec3 Mask(vec2 pos){ + pos.x+=pos.y*3.0; + vec3 mask=vec3(maskDark,maskDark,maskDark); + pos.x=fract(pos.x/6.0); + if(pos.x<0.333)mask.r=maskLight; + else if(pos.x<0.666)mask.g=maskLight; + else mask.b=maskLight; + return mask;} + +// Draw dividing bars. +float Bar(float pos,float bar){pos-=bar;return pos*pos<4.0?0.0:1.0;} + +// Entry. +void main(){ + // Unmodified. + vec2 pos=Warp(v_texCoord); + vec4 fragColor; + fragColor.rgb=Tri(pos)*Mask(gl_FragCoord.xy); + fragColor.rgb=ToSrgb(fragColor.rgb); + gl_FragColor=v_color * vec4(fragColor.rgb, 1.0); +} diff --git a/image.png b/image.png new file mode 100644 index 0000000..4e68e42 Binary files /dev/null and b/image.png differ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..17bc1a1 --- /dev/null +++ b/main.cpp @@ -0,0 +1,337 @@ +#include + +#include +#include +#include + +#include +#include + +#ifdef __APPLE__ +#include "CoreFoundation/CoreFoundation.h" +#include + +#if ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 +#include +#else +#include +#endif //!ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 +#else +#include +#include +#endif + +const int WIN_WIDTH = 640; +const int WIN_HEIGHT = 480; + +const int WORLD_WIDTH = 320; +const int WORLD_HEIGHT = 240; + +struct sprite { + float x; + float y; + float vx; + float vy; +}; + +const int MAX_SPRITES = 20; + +#ifndef __APPLE__ + +// I'm avoiding the use of GLEW or some extensions handler, but that +// doesn't mean you should... +PFNGLCREATESHADERPROC glCreateShader; +PFNGLSHADERSOURCEPROC glShaderSource; +PFNGLCOMPILESHADERPROC glCompileShader; +PFNGLGETSHADERIVPROC glGetShaderiv; +PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; +PFNGLDELETESHADERPROC glDeleteShader; +PFNGLATTACHSHADERPROC glAttachShader; +PFNGLCREATEPROGRAMPROC glCreateProgram; +PFNGLLINKPROGRAMPROC glLinkProgram; +PFNGLVALIDATEPROGRAMPROC glValidateProgram; +PFNGLGETPROGRAMIVPROC glGetProgramiv; +PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; +PFNGLUSEPROGRAMPROC glUseProgram; + +bool initGLExtensions() { + glCreateShader = (PFNGLCREATESHADERPROC)SDL_GL_GetProcAddress("glCreateShader"); + glShaderSource = (PFNGLSHADERSOURCEPROC)SDL_GL_GetProcAddress("glShaderSource"); + glCompileShader = (PFNGLCOMPILESHADERPROC)SDL_GL_GetProcAddress("glCompileShader"); + glGetShaderiv = (PFNGLGETSHADERIVPROC)SDL_GL_GetProcAddress("glGetShaderiv"); + glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)SDL_GL_GetProcAddress("glGetShaderInfoLog"); + glDeleteShader = (PFNGLDELETESHADERPROC)SDL_GL_GetProcAddress("glDeleteShader"); + glAttachShader = (PFNGLATTACHSHADERPROC)SDL_GL_GetProcAddress("glAttachShader"); + glCreateProgram = (PFNGLCREATEPROGRAMPROC)SDL_GL_GetProcAddress("glCreateProgram"); + glLinkProgram = (PFNGLLINKPROGRAMPROC)SDL_GL_GetProcAddress("glLinkProgram"); + glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)SDL_GL_GetProcAddress("glValidateProgram"); + glGetProgramiv = (PFNGLGETPROGRAMIVPROC)SDL_GL_GetProcAddress("glGetProgramiv"); + glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)SDL_GL_GetProcAddress("glGetProgramInfoLog"); + glUseProgram = (PFNGLUSEPROGRAMPROC)SDL_GL_GetProcAddress("glUseProgram"); + + return glCreateShader && glShaderSource && glCompileShader && glGetShaderiv && + glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram && + glLinkProgram && glValidateProgram && glGetProgramiv && glGetProgramInfoLog && + glUseProgram; +} + +#endif + +GLuint compileShader(const char* source, GLuint shaderType) { + std::cout << "Compilando shader:" << std::endl << source << std::endl; + // Create ID for shader + GLuint result = glCreateShader(shaderType); + // Define shader text + glShaderSource(result, 1, &source, NULL); + // Compile shader + glCompileShader(result); + + //Check vertex shader for errors + GLint shaderCompiled = GL_FALSE; + glGetShaderiv( result, GL_COMPILE_STATUS, &shaderCompiled ); + if( shaderCompiled != GL_TRUE ) { + std::cout << "Error en la compilación: " << result << "!" << std::endl; + GLint logLength; + glGetShaderiv(result, GL_INFO_LOG_LENGTH, &logLength); + if (logLength > 0) + { + GLchar *log = (GLchar*)malloc(logLength); + glGetShaderInfoLog(result, logLength, &logLength, log); + std::cout << "Shader compile log:" << log << std::endl; + free(log); + } + glDeleteShader(result); + result = 0; + } else { + std::cout << "Shader compilado correctamente. Id = " << result << std::endl; + } + return result; +} + +GLuint compileProgram(const char* vtxFile, const char* fragFile) { + GLuint programId = 0; + GLuint vtxShaderId, fragShaderId; + + programId = glCreateProgram(); + + std::ifstream f(vtxFile); + std::string source((std::istreambuf_iterator(f)), + std::istreambuf_iterator()); + vtxShaderId = compileShader(source.c_str(), GL_VERTEX_SHADER); + + f=std::ifstream(fragFile); + source=std::string((std::istreambuf_iterator(f)), + std::istreambuf_iterator()); + fragShaderId = compileShader(source.c_str(), GL_FRAGMENT_SHADER); + + if(vtxShaderId && fragShaderId) { + // Associate shader with program + glAttachShader(programId, vtxShaderId); + glAttachShader(programId, fragShaderId); + glLinkProgram(programId); + glValidateProgram(programId); + + // Check the status of the compile/link + GLint logLen; + glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &logLen); + if(logLen > 0) { + char* log = (char*) malloc(logLen * sizeof(char)); + // Show any errors as appropriate + glGetProgramInfoLog(programId, logLen, &logLen, log); + std::cout << "Prog Info Log: " << std::endl << log << std::endl; + free(log); + } + } + if(vtxShaderId) { + glDeleteShader(vtxShaderId); + } + if(fragShaderId) { + glDeleteShader(fragShaderId); + } + return programId; +} + +void presentBackBuffer(SDL_Renderer *renderer, SDL_Window* win, SDL_Texture* backBuffer, GLuint programId) { + GLint oldProgramId; + // Guarrada para obtener el textureid (en driverdata->texture) + //Detach the texture + SDL_SetRenderTarget(renderer, NULL); + SDL_RenderClear(renderer); + + SDL_GL_BindTexture(backBuffer, NULL, NULL); + if(programId != 0) { + glGetIntegerv(GL_CURRENT_PROGRAM,&oldProgramId); + glUseProgram(programId); + } + + GLfloat minx, miny, maxx, maxy; + GLfloat minu, maxu, minv, maxv; + + // Coordenadas de la ventana donde pintar. + minx = 0.0f; + miny = 0.0f; + maxx = WIN_WIDTH; + maxy = WIN_HEIGHT; + + minu = 0.0f; + maxu = 1.0f; + minv = 0.0f; + maxv = 1.0f; + + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(minu, minv); + glVertex2f(minx, miny); + glTexCoord2f(maxu, minv); + glVertex2f(maxx, miny); + glTexCoord2f(minu, maxv); + glVertex2f(minx, maxy); + glTexCoord2f(maxu, maxv); + glVertex2f(maxx, maxy); + glEnd(); + SDL_GL_SwapWindow(win); + + if(programId != 0) { + glUseProgram(oldProgramId); + } +} + +#ifdef __APPLE__ +void initializeFileSystem() { + CFBundleRef mainBundle = CFBundleGetMainBundle(); + CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle); + char path[PATH_MAX]; + if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, PATH_MAX)) + { + std::cerr << "Couldn't get file system representation! " << std::endl; + } + CFRelease(resourcesURL); + chdir(path); +} +#endif + +int main(int argc, char **argv){ + GLuint programId; + +#ifdef __APPLE__ + initializeFileSystem(); +#endif + + if (SDL_Init(SDL_INIT_EVERYTHING | SDL_VIDEO_OPENGL) != 0){ + std::cerr << "SDL_Init failed: " << SDL_GetError() << "\n"; + return 1; + } + + SDL_Window *win = SDL_CreateWindow("Custom shader with SDL2 renderer!", SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, WIN_WIDTH, WIN_HEIGHT, 0); + + SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); + + SDL_Renderer *renderer = SDL_CreateRenderer(win, -1, + SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); + + SDL_RendererInfo rendererInfo; + SDL_GetRendererInfo(renderer, &rendererInfo); + + if(!strncmp(rendererInfo.name, "opengl", 6)) { + std::cout << "Es OpenGL!" << std::endl; +#ifndef __APPLE__ + // If you want to use GLEW or some other GL extension handler, do it here! + if (!initGLExtensions()) { + std::cout << "Couldn't init GL extensions!" << std::endl; + SDL_Quit(); + exit(-1); + } +#endif + // Compilar el shader y dejarlo listo para usar. + programId = compileProgram("std.vertex", "crt.fragment"); + std::cout << "programId = " << programId << std::endl; + } + + //Put your own bmp image here + SDL_Surface *bmpSurf = IMG_Load("image.png"); + SDL_Texture *bmpTex = SDL_CreateTextureFromSurface(renderer, bmpSurf); + SDL_FreeSurface(bmpSurf); + + //Make a target texture to render too + SDL_Texture *texTarget = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, + SDL_TEXTUREACCESS_TARGET, WORLD_WIDTH, WORLD_HEIGHT); + + int done = 0; + int useShader = 0; + SDL_Rect targetRect; + + targetRect.x = 0; targetRect.y=0; targetRect.w=32; targetRect.h=32; + + struct sprite sprites[MAX_SPRITES]; + + for(int i=0;i < MAX_SPRITES;++i) { + sprites[i].x = (float) (rand() % (WORLD_WIDTH - targetRect.w)); + sprites[i].y = (float) (rand() % (WORLD_HEIGHT - targetRect.h)); + sprites[i].vx = (float) (rand() % 5 / 10.0f) - 0.2f; + sprites[i].vy = (float) (rand() % 5 / 10.0f) - 0.2f; + } + + // Voy a poner el fondo blanco para que se vea el shader + SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); + + while ( ! done ) { + //Render to the texture + SDL_SetRenderTarget(renderer, texTarget); + SDL_RenderClear(renderer); + + // Pintamos MAX_SPRITES UWOLes + struct sprite* spr; + for(int i=0;i < MAX_SPRITES;++i) { + spr = &(sprites[i]); + spr->x += spr->vx; + if(spr->x < 0) { + spr->x = 0; + spr->vx *= -1; + } + if(spr->x > WORLD_WIDTH - targetRect.w) { + spr->x = (float) (WORLD_WIDTH - targetRect.w); + spr->vx *= -1; + } + spr->y += spr->vy; + if(spr->y < 0) { + spr->y = 0; + spr->vy *= -1; + } + if(spr->y > WORLD_HEIGHT - targetRect.h) { + spr->y = (float) (WORLD_HEIGHT - targetRect.h); + spr->vy *= -1; + } + + targetRect.x = (int)spr->x; + targetRect.y = (int)spr->y; + + SDL_RenderCopy(renderer, bmpTex, NULL, &targetRect); + } + + presentBackBuffer(renderer, win, texTarget, programId); + + /* This could go in a separate function */ + SDL_Event event; + while ( SDL_PollEvent(&event) ) { + if ( event.type == SDL_QUIT ) { + done = 1; + } + if ( event.type == SDL_KEYDOWN ) { + if ( event.key.keysym.sym == SDLK_SPACE ) { + useShader ^= 1; + std::cout << "useShader = " << (useShader ? "true" : "false") << std::endl; + } + if ( event.key.keysym.sym == SDLK_ESCAPE ) { + done = 1; + } + } + } + } + + SDL_DestroyTexture(texTarget); + SDL_DestroyTexture(bmpTex); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(win); + SDL_Quit(); + return 0; +} \ No newline at end of file diff --git a/std.vertex b/std.vertex new file mode 100644 index 0000000..75ebe82 --- /dev/null +++ b/std.vertex @@ -0,0 +1,9 @@ +varying vec4 v_color; +varying vec2 v_texCoord; + +void main() +{ + gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; + v_color = gl_Color; + v_texCoord = vec2(gl_MultiTexCoord0); +} \ No newline at end of file