Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
An example of using OpenGL shaders (and indexed drawing) in Nim
# 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()
#version 300 es
precision mediump float;
in vec4 color;
out vec4 fragColor;
void main() {
fragColor = color;
}
#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
You can’t perform that action at this time.