Last active
September 26, 2019 05:59
-
-
Save define-private-public/c653310208c517c8f115d17550db5e33 to your computer and use it in GitHub Desktop.
An example of using OpenGL shaders (and indexed drawing) in Nim
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
# File: nim_opengl_shader_example.nim | |
# Author: Benjamin N. Summerton <define-private-public> | |
# Description: An example of how to use OpenGL shaders in Nim. Draws a simple | |
# rotating hexagon with 6 colors. Also uses indexed drawing. | |
# | |
# Uses this GLFW binding: https://github.com/rafaelvasco/nimrod-glfw, though if | |
# want to use SDL2, changing out the windowing should be simple. | |
# | |
# This was built on Linux using OpenGL ES 3. If you want to use a different | |
# OpenGL version then you will have to alter the shader source. | |
# | |
# If you want to turn on Anti-Alising, add "aa" in the command line. If you | |
# want to change the rotation speed, input a non-negative floating point number. | |
# Enter "0" if you don't want the hexagon to rotate. | |
# | |
# At the time of writing. This file used Nim 0.15.x | |
include system/timers | |
import math | |
import parseopt2 | |
import strutils | |
import opengl | |
import glfw3 as glfw | |
# Runtime command line arguments | |
var | |
useAA = false # Anti-Aliasing | |
rotationSpeed:float = 5 # sec/rotation | |
# Get CLI options | |
for kind, key, value in getopt(): | |
if kind == cmdArgument: | |
if key == "aa": | |
# enable Anti-Aliasing | |
useAA = true | |
else: | |
# Assume it's the rotation speed | |
rotationSpeed = key.parseFloat | |
if rotationSpeed < 0: | |
rotationSpeed = 0 | |
# init libraries | |
if glfw.Init() == 0: | |
raise newException(Exception, "Failed to intialize GLFW") | |
# Turn on anti-aliasing? | |
if useAA: | |
glfw.WindowHint(glfw.SAMPLES, 16) | |
# Open a window | |
var window = glfw.CreateWindow(800, 800, "OpenGL/GLFW Shader Example (in Nim)", nil, nil) | |
glfw.MakeContextCurrent(window) | |
# Load opengl | |
loadExtensions() | |
# Print some info | |
echo(glfw.GetVersionString()) | |
# The data for (and about) OpenGL | |
var | |
# Vertex data | |
vertices: array[21, GLfloat] = [ | |
# Center point | |
0'f32, 0'f32, 0'f32, | |
# Outer points | |
1 * cos(0 * (PI / 3)).GLfloat, 1 * sin(0 * (PI / 3)).GLfloat, 0'f32, | |
1 * cos(1 * (PI / 3)).GLfloat, 1 * sin(1 * (PI / 3)).GLfloat, 0'f32, | |
1 * cos(2 * (PI / 3)).GLfloat, 1 * sin(2 * (PI / 3)).GLfloat, 0'f32, | |
1 * cos(3 * (PI / 3)).GLfloat, 1 * sin(3 * (PI / 3)).GLfloat, 0'f32, | |
1 * cos(4 * (PI / 3)).GLfloat, 1 * sin(4 * (PI / 3)).GLfloat, 0'f32, | |
1 * cos(5 * (PI / 3)).GLfloat, 1 * sin(5 * (PI / 3)).GLfloat, 0'f32, | |
] | |
# Color data | |
colors: array[21, GLfloat] = [ | |
# Center point | |
1'f32, 1'f32, 1'f32, # White | |
# Outer points | |
1'f32, 0'f32, 0'f32, # Red | |
1'f32, 1'f32, 0'f32, # Yellow | |
0'f32, 1'f32, 0'f32, # Green | |
0'f32, 1'f32, 1'f32, # Cyan | |
0'f32, 0'f32, 1'f32, # Blue | |
1'f32, 0'f32, 1'f32, # Magenta | |
] | |
# Index data | |
indices: array[8, GLushort] = [ | |
0'u16, | |
1'u16, | |
2'u16, | |
3'u16, | |
4'u16, | |
5'u16, | |
6'u16, | |
1'u16 | |
] | |
# OpenGL data | |
vertexVBO: GLuint = 0 | |
colorVBO: GLuint = 0 | |
vao: GLuint = 0 | |
vertShader: GLuint | |
fragShader: GLuint | |
shaderProgram: GLuint | |
# For rotating the model | |
rotation: GLfloat = 0.0 | |
rotationLoc: GLint | |
# Shader source | |
vertShaderSrc = readFile("shader.vert") | |
fragShaderSrc = readFile("shader.frag") | |
vertShaderArray = allocCStringArray([vertShaderSrc]) # dealloc'd at the end | |
fragShaderArray = allocCStringArray([fragShaderSrc]) # dealloc'd at the end | |
# Status variables | |
isCompiled: GLint | |
isLinked: GLint | |
# Bind the vertices | |
glGenBuffers(1, vertexVBO.addr) | |
glBindBuffer(GL_ARRAY_BUFFER, vertexVBO) | |
glBufferData(GL_ARRAY_BUFFER, vertices.sizeof, vertices.addr, GL_STATIC_DRAW) | |
# Bind the colors | |
glGenBuffers(1, colorVBO.addr) | |
glBindBuffer(GL_ARRAY_BUFFER, colorVBO) | |
glBufferData(GL_ARRAY_BUFFER, colors.sizeof, colors.addr, GL_STATIC_DRAW) | |
# The array object | |
glGenVertexArrays(1, vao.addr) | |
glBindVertexArray(vao) | |
glBindBuffer(GL_ARRAY_BUFFER, vertexVBO); | |
glVertexAttribPointer(0, 3, cGL_FLOAT, GL_FALSE, 0, nil) | |
glBindBuffer(GL_ARRAY_BUFFER, colorVBO); | |
glVertexAttribPointer(1, 3, cGL_FLOAT, GL_FALSE, 0, nil) | |
glEnableVertexAttribArray(0) | |
glEnableVertexAttribArray(1) | |
# Compile shaders | |
# Vertex | |
vertShader = glCreateShader(GL_VERTEX_SHADER) | |
glShaderSource(vertShader, 1, vertShaderArray, nil) | |
glCompileShader(vertShader) | |
glGetShaderiv(vertShader, GL_COMPILE_STATUS, isCompiled.addr) | |
# Check vertex compilation status | |
if isCompiled == 0: | |
echo "Vertex Shader wasn't compiled. Reason:" | |
# Query the log size | |
var logSize: GLint | |
glGetShaderiv(vertShader, GL_INFO_LOG_LENGTH, logSize.addr) | |
# Get the log itself | |
var | |
logStr = cast[ptr GLchar](alloc(logSize)) | |
logLen: GLsizei | |
glGetShaderInfoLog(vertShader, logSize.GLsizei, logLen.addr, logStr) | |
# Print the log | |
echo $logStr | |
# Cleanup | |
dealloc(logStr) | |
else: | |
echo "Vertex Shader compiled successfully." | |
# Fragment | |
fragShader = glCreateShader(GL_FRAGMENT_SHADER) | |
glShaderSource(fragShader, 1, fragShaderArray, nil) | |
glCompileShader(fragShader) | |
glGetShaderiv(fragShader, GL_COMPILE_STATUS, isCompiled.addr) | |
# Check Fragment compilation status | |
if isCompiled == 0: | |
echo "Fragment Shader wasn't compiled. Reason:" | |
# Query the log size | |
var logSize: GLint | |
glGetShaderiv(fragShader, GL_INFO_LOG_LENGTH, logSize.addr) | |
# Get the log itself | |
var | |
logStr = cast[ptr GLchar](alloc(logSize)) | |
logLen: GLsizei | |
glGetShaderInfoLog(fragShader, logSize.GLsizei, logLen.addr, logStr) | |
# Print the log | |
echo $logStr | |
# Cleanup | |
dealloc(logStr) | |
else: | |
echo "Fragment Shader compiled successfully." | |
# Attach to a GL program | |
shaderProgram = glCreateProgram() | |
glAttachShader(shaderProgram, vertShader); | |
glAttachShader(shaderProgram, fragShader); | |
# insert locations | |
glBindAttribLocation(shaderProgram, 0, "vertexPos"); | |
glBindAttribLocation(shaderProgram, 0, "vertexClr"); | |
glLinkProgram(shaderProgram); | |
# Check for shader linking errors | |
glGetProgramiv(shaderProgram, GL_LINK_STATUS, isLinked.addr) | |
if isLinked == 0: | |
echo "Wasn't able to link shaders. Reason:" | |
# Get the log size | |
var logSize: GLint | |
glGetProgramiv(shaderProgram, GL_INFO_LOG_LENGTH, logSize.addr) | |
# Get the log itself | |
var | |
logStr = cast[ptr GLchar](alloc(logSize)) | |
logLen: GLsizei | |
glGetProgramInfoLog(shaderProgram, logSize.GLsizei, logLen.addr, logStr) | |
# Print the log | |
echo $logStr | |
# cleanup | |
dealloc(logStr) | |
else: | |
echo "Shader Program ready!" | |
# If everything is linked, that means we're good to go! | |
if isLinked == 1: | |
# Get the rotation location | |
rotationLoc = glGetUniformLocation(shaderProgram, "rotation"); | |
# Some other options | |
if useAA: | |
glEnable(GL_MULTISAMPLE) | |
echo "Using Anti-Aliasing." | |
# Record the time | |
var | |
startTicks = getTicks() | |
curTicks = startTicks | |
# Main loop | |
while glfw.WindowShouldClose(window) == 0: | |
curTicks = getTicks() | |
# Exit on `ESC` press | |
if glfw.GetKey(window, glfw.KEY_ESCAPE) == 1: | |
glfw.SetWindowShouldClose(window, 1) | |
# Clear and setup drawing | |
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT) | |
glUseProgram(shaderProgram) | |
# Update rotation | |
let secs = (curTicks - startTicks).float / 1_000_000_000.0 | |
if rotationSpeed != 0: | |
rotation = 2 * PI * (secs / rotationSpeed) | |
else: | |
rotation = 0 | |
# Do the drawing | |
glBindVertexArray(vao) | |
glUniform1f(rotationLoc, rotation) | |
glDrawElements(GL_TRIANGLE_FAN, indices.len.GLsizei, GL_UNSIGNED_SHORT, indices.addr) | |
# Unbind | |
glBindVertexArray(0); | |
glUseProgram(0); | |
# Poll and swap | |
glfw.PollEvents() | |
glfw.SwapBuffers(window) | |
# Cleanup non-GC'd stuff | |
deallocCStringArray(vertShaderArray) | |
deallocCStringArray(fragShaderArray) | |
# Cleanup OpenGL Stuff | |
glDeleteProgram(shaderProgram) | |
glDeleteShader(vertShader) | |
glDeleteShader(fragShader) | |
glDeleteBuffers(1, vertexVBO.addr) | |
glDeleteBuffers(1, colorVBO.addr) | |
glDeleteVertexArrays(1, vao.addr) | |
# cleanup GLFW | |
glfw.DestroyWindow(window) | |
glfw.Terminate() | |
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
#version 300 es | |
precision mediump float; | |
in vec4 color; | |
out vec4 fragColor; | |
void main() { | |
fragColor = color; | |
} | |
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
#version 300 es | |
layout(location = 0) in vec3 vertexPos; | |
layout(location = 1) in vec3 vertexClr; | |
uniform float rotation; | |
out vec4 color; | |
void main() { | |
mat3 rotateAboutZ= mat3( | |
cos(rotation), -sin(rotation), 0, | |
sin(rotation), cos(rotation), 0, | |
0, 0, 1 | |
); | |
// Rotate clockwise | |
gl_Position = vec4(vertexPos * inverse(rotateAboutZ), 1); | |
color = vec4(vertexClr, 1); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment