Skip to content

Instantly share code, notes, and snippets.

@ClickerMonkey
Last active December 29, 2015 03:08
Show Gist options
  • Save ClickerMonkey/7605059 to your computer and use it in GitHub Desktop.
Save ClickerMonkey/7605059 to your computer and use it in GitHub Desktop.
Shader Fun
package com.axe.sandbox.gs;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import org.lwjgl.BufferUtils;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.ARBBufferObject;
import org.lwjgl.opengl.ARBFragmentShader;
import org.lwjgl.opengl.ARBGeometryShader4;
import org.lwjgl.opengl.ARBShaderObjects;
import org.lwjgl.opengl.ARBVertexBufferObject;
import org.lwjgl.opengl.ARBVertexShader;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL14;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL21;
import org.lwjgl.opengl.GL30;
import org.lwjgl.opengl.GL31;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL40;
import org.lwjgl.opengl.GL42;
import org.lwjgl.util.glu.GLU;
public class PointToCube
{
public static final int GREEN = -16744448;
public static final int BLUE = -65536;
private boolean done = false;
private boolean fullscreen = false;
private final String windowTitle = "Point to Cube";
private boolean f1 = false;
private DisplayMode displayMode;
private int program;
private int vbo;
private ByteBuffer vboData = BufferUtils.createByteBuffer( 1024 );
public static void main(String args[]) {
boolean fullscreen = false;
if(args.length>0) {
if(args[0].equalsIgnoreCase("fullscreen")) {
fullscreen = true;
}
}
PointToCube l5 = new PointToCube();
l5.run(fullscreen);
}
public void run(boolean fullscreen) {
this.fullscreen = fullscreen;
try {
init();
while (!done) {
mainloop();
render();
Display.update();
}
cleanup();
}
catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
private void mainloop() {
if(Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {
done = true;
}
if(Display.isCloseRequested()) {
done = true;
}
if(Keyboard.isKeyDown(Keyboard.KEY_F1) && !f1) {
f1 = true;
switchMode();
}
if(!Keyboard.isKeyDown(Keyboard.KEY_F1)) {
f1 = false;
}
}
private void switchMode() {
fullscreen = !fullscreen;
try {
Display.setFullscreen(fullscreen);
}
catch(Exception e) {
e.printStackTrace();
}
}
private boolean render() {
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GL11.glLoadIdentity();
rotate();
ARBShaderObjects.glUseProgramObjectARB( program );
ARBBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, vbo);
GL11.glEnableClientState( GL11.GL_VERTEX_ARRAY );
GL11.glEnableClientState( GL11.GL_COLOR_ARRAY );
GL11.glEnableClientState( GL14.GL_SECONDARY_COLOR_ARRAY );
GL11.glVertexPointer( 3, GL11.GL_FLOAT, 32, 0 );
GL11.glColorPointer( 4, GL11.GL_UNSIGNED_BYTE, 32, 12 );
GL14.glSecondaryColorPointer( 4, GL11.GL_FLOAT, 32, 16 );
GL11.glDrawArrays( GL11.GL_POINTS, 0, 2 );
GL11.glDisableClientState( GL14.GL_SECONDARY_COLOR_ARRAY );
GL11.glDisableClientState( GL11.GL_COLOR_ARRAY );
GL11.glDisableClientState( GL11.GL_VERTEX_ARRAY );
ARBBufferObject.glBindBufferARB(ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, 0);
ARBShaderObjects.glUseProgramObjectARB( 0 );
return true;
}
private void createWindow() throws Exception {
Display.setFullscreen(fullscreen);
DisplayMode d[] = Display.getAvailableDisplayModes();
for (int i = 0; i < d.length; i++) {
if (d[i].getWidth() == 640
&& d[i].getHeight() == 480
&& d[i].getBitsPerPixel() == 32) {
displayMode = d[i];
break;
}
}
Display.setDisplayMode(displayMode);
Display.setTitle(windowTitle);
Display.create();
}
private void init() throws Exception {
createWindow();
initGL();
initShader();
initVertexBufferObject();
}
private void initGL() {
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glShadeModel(GL11.GL_SMOOTH);
GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
GL11.glClearDepth(1.0);
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glDepthFunc(GL11.GL_LEQUAL);
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GLU.gluPerspective(
45.0f,
(float) displayMode.getWidth() / (float) displayMode.getHeight(),
0.001f,
1000.0f);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST);
}
private static void cleanup() {
Display.destroy();
}
private void initShader() throws Exception {
int geometryShader = createShader( "shader.gs", ARBGeometryShader4.GL_GEOMETRY_SHADER_ARB );
int vertexShader = createShader( "shader.vs", ARBVertexShader.GL_VERTEX_SHADER_ARB );
int fragmentShader = createShader( "shader.fs", ARBFragmentShader.GL_FRAGMENT_SHADER_ARB );
program = ARBShaderObjects.glCreateProgramObjectARB();
ARBShaderObjects.glAttachObjectARB(program, geometryShader);
ARBShaderObjects.glAttachObjectARB(program, vertexShader);
ARBShaderObjects.glAttachObjectARB(program, fragmentShader);
GL20.glDeleteShader( geometryShader );
GL20.glDeleteShader( vertexShader );
GL20.glDeleteShader( fragmentShader );
ARBShaderObjects.glLinkProgramARB(program);
if (ARBShaderObjects.glGetObjectParameteriARB(program, ARBShaderObjects.GL_OBJECT_LINK_STATUS_ARB) == GL11.GL_FALSE) {
System.err.println(getLogInfo(program));
}
int attribCount = ARBShaderObjects.glGetObjectParameteriARB( program, GL20.GL_ACTIVE_ATTRIBUTES);
int attribNameMaxLength = Math.max( 128, GL20.glGetProgram( program, GL20.GL_ACTIVE_ATTRIBUTE_MAX_LENGTH ) );
for (int i = 0; i < attribCount; i++) {
String attribName = GL20.glGetActiveAttrib( program, i, attribNameMaxLength );
int attribSize = GL20.glGetActiveAttribSize( program, i );
int attribType = GL20.glGetActiveAttribType( program, i );
System.out.println( attribName + " (" + attribSize + "): " + GL_TYPE_MAP.get(attribType) );
}
int uniformCount = ARBShaderObjects.glGetObjectParameteriARB( program, GL20.GL_ACTIVE_UNIFORMS );
int uniformNameMaxLength = Math.max( 128, GL20.glGetProgram( program, GL20.GL_ACTIVE_UNIFORM_MAX_LENGTH ) );
for (int i = 0; i < uniformCount; i++) {
String uniformName = GL20.glGetActiveUniform( program, i, uniformNameMaxLength );
int uniformSize = GL20.glGetActiveUniformSize( program, i );
int uniformType = GL20.glGetActiveUniformType( program, i );
System.out.println( uniformName + " (" + uniformSize + "): " + GL_TYPE_MAP.get(uniformType) );
}
checkError();
}
private void initVertexBufferObject() {
vbo = ARBBufferObject.glGenBuffersARB();
addPoint( -1.5f, 0.0f, -4.0f, GREEN, 0.0f, 0.0f, 0.0f, 0.5f );
addPoint( 1.5f, 0.0f, -4.0f, BLUE, 0.0f, 0.0f, 0.0f, 1.5f );
vboData.flip();
ARBBufferObject.glBindBufferARB( ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, vbo );
ARBBufferObject.glBufferDataARB( ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, vboData, ARBBufferObject.GL_DYNAMIC_DRAW_ARB );
ARBBufferObject.glBindBufferARB( ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, 0 );
checkError();
}
private void addPoint(float x, float y, float z, int color, float pitch, float yaw, float roll, float scale)
{
vboData.putFloat( x ).putFloat( y ).putFloat( z ); // position
vboData.putInt( color ); // color
vboData.putFloat( pitch ).putFloat( yaw ).putFloat( roll ); // rotation
vboData.putFloat( scale ); // scale
}
private void rotate() {
vboData.putFloat( 16, vboData.getFloat( 16 ) - 0.001f );
vboData.putFloat( 20, vboData.getFloat( 20 ) + 0.002f );
vboData.putFloat( 24, vboData.getFloat( 24 ) + 0.001f );
vboData.putFloat( 16 + 32, vboData.getFloat( 16 + 32 ) + 0.001f );
vboData.putFloat( 20 + 32, vboData.getFloat( 20 + 32 ) - 0.002f );
vboData.putFloat( 24 + 32, vboData.getFloat( 24 + 32 ) - 0.001f );
ARBBufferObject.glBindBufferARB( ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, vbo );
ARBBufferObject.glBufferDataARB( ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, vboData, ARBBufferObject.GL_DYNAMIC_DRAW_ARB );
ARBBufferObject.glBindBufferARB( ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, 0 );
checkError();
}
private static int createShader(String filename, int type) throws Exception {
String code = getCode( filename );
int shader = ARBShaderObjects.glCreateShaderObjectARB( type );
ARBShaderObjects.glShaderSourceARB( shader, code );
ARBShaderObjects.glCompileShaderARB( shader );
if (ARBShaderObjects.glGetObjectParameteriARB(shader, ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB) == GL11.GL_FALSE)
throw new RuntimeException("Error creating shader: " + getLogInfo(shader));
return shader;
}
private static String getCode(String filename) throws Exception {
InputStream is = PointToCube.class.getClassLoader().getResourceAsStream( filename );
byte[] chunk = new byte[4096];
StringBuilder sb = new StringBuilder();
int read = -1;
while ((read = is.read( chunk )) != -1) {
sb.append(new String(chunk, 0, read));
}
return sb.toString();
}
private static void checkError() {
int error = -1;
while ((error = GL11.glGetError()) != GL11.GL_NO_ERROR) {
System.out.println( GLU.gluErrorString( error ) );
}
}
private static String getLogInfo(int obj) {
return ARBShaderObjects.glGetInfoLogARB(obj, ARBShaderObjects.glGetObjectParameteriARB(obj, ARBShaderObjects.GL_OBJECT_INFO_LOG_LENGTH_ARB));
}
@SuppressWarnings ("serial" )
private static Map<Integer, String> GL_TYPE_MAP = new HashMap<Integer, String>() {{
put( GL11.GL_FLOAT, "float" );
put( GL20.GL_FLOAT_VEC2, "vec2" );
put( GL20.GL_FLOAT_VEC3, "vec3" );
put( GL20.GL_FLOAT_VEC4, "vec4" );
put( GL20.GL_FLOAT_MAT2, "mat2" );
put( GL20.GL_FLOAT_MAT3, "mat3" );
put( GL20.GL_FLOAT_MAT4, "mat4" );
put( GL21.GL_FLOAT_MAT2x4, "mat2x4" );
put( GL21.GL_FLOAT_MAT3x2, "mat3x2" );
put( GL21.GL_FLOAT_MAT3x4, "mat3x4" );
put( GL21.GL_FLOAT_MAT4x2, "mat4x2" );
put( GL21.GL_FLOAT_MAT4x3, "mat4x3" );
put( GL11.GL_INT, "int" );
put( GL20.GL_INT_VEC2, "ivec2" );
put( GL20.GL_INT_VEC3, "ivec3" );
put( GL20.GL_INT_VEC4, "ivec4" );
put( GL11.GL_UNSIGNED_INT, "unsigned int" );
put( GL30.GL_UNSIGNED_INT_VEC2, "uvec2" );
put( GL30.GL_UNSIGNED_INT_VEC3, "uvec3" );
put( GL30.GL_UNSIGNED_INT_VEC4, "uvec4" );
put( GL11.GL_DOUBLE, "double" );
put( GL40.GL_DOUBLE_VEC2, "dvec2" );
put( GL40.GL_DOUBLE_VEC3, "dvec3" );
put( GL40.GL_DOUBLE_VEC4, "dvec4" );
put( GL40.GL_DOUBLE_MAT2, "dmat2" );
put( GL40.GL_DOUBLE_MAT3, "dmat3" );
put( GL40.GL_DOUBLE_MAT4, "dmat4" );
put( GL40.GL_DOUBLE_MAT2x3, "dmat2x3" );
put( GL40.GL_DOUBLE_MAT2x4, "dmat2x4" );
put( GL40.GL_DOUBLE_MAT3x2, "dmat3x2" );
put( GL40.GL_DOUBLE_MAT3x4, "dmat3x4" );
put( GL40.GL_DOUBLE_MAT4x2, "dmat4x2" );
put( GL40.GL_DOUBLE_MAT4x3, "dmat4x3" );
put( GL20.GL_BOOL, "bool" );
put( GL20.GL_BOOL_VEC2, "bvec2" );
put( GL20.GL_BOOL_VEC3, "bvec3" );
put( GL20.GL_BOOL_VEC4, "bvec4" );
put( GL20.GL_SAMPLER_1D, "sampler1D" );
put( GL20.GL_SAMPLER_2D, "sampler2D" );
put( GL20.GL_SAMPLER_3D, "sampler3D" );
put( GL20.GL_SAMPLER_CUBE, "samplerCube" );
put( GL20.GL_SAMPLER_1D_SHADOW, "sampler1DShadow" );
put( GL20.GL_SAMPLER_2D_SHADOW, "sampler2DShadow" );
put( GL30.GL_SAMPLER_1D_ARRAY, "sampler1DArray" );
put( GL30.GL_SAMPLER_2D_ARRAY, "sampler2DArray" );
put( GL30.GL_SAMPLER_1D_ARRAY_SHADOW, "sampler1DArrayShadow" );
put( GL30.GL_SAMPLER_2D_ARRAY_SHADOW, "sampler2DArrayShadow" );
put( GL32.GL_SAMPLER_2D_MULTISAMPLE, "sampler2DMS" );
put( GL32.GL_SAMPLER_2D_MULTISAMPLE_ARRAY, "sampler2DMSArray" );
put( GL30.GL_SAMPLER_CUBE_SHADOW, "samplerCubeShadow" );
put( GL30.GL_SAMPLER_BUFFER, "samplerBuffer" );
put( GL31.GL_SAMPLER_2D_RECT, "sampler2DRect" );
put( GL31.GL_SAMPLER_2D_RECT_SHADOW, "sampler2DRectShadow" );
put( GL30.GL_INT_SAMPLER_1D, "isampler1D" );
put( GL30.GL_INT_SAMPLER_2D, "isampler2D" );
put( GL30.GL_INT_SAMPLER_3D, "isampler3D" );
put( GL30.GL_INT_SAMPLER_CUBE, "isamplerCube" );
put( GL30.GL_INT_SAMPLER_1D_ARRAY, "isampler1DArray" );
put( GL30.GL_INT_SAMPLER_2D_ARRAY, "isampler2DArray" );
put( GL32.GL_INT_SAMPLER_2D_MULTISAMPLE, "isampler2DMS" );
put( GL32.GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY, "isampler2DMSArray" );
put( GL30.GL_INT_SAMPLER_BUFFER, "isamplerBuffer" );
put( GL30.GL_INT_SAMPLER_2D_RECT, "isampler2DRect" );
put( GL30.GL_UNSIGNED_INT_SAMPLER_1D, "usampler1D" );
put( GL30.GL_UNSIGNED_INT_SAMPLER_2D, "usampler2D" );
put( GL30.GL_UNSIGNED_INT_SAMPLER_3D, "usampler3D" );
put( GL30.GL_UNSIGNED_INT_SAMPLER_CUBE, "usamplerCube" );
put( GL30.GL_UNSIGNED_INT_SAMPLER_1D_ARRAY, "usampler2DArray" );
put( GL30.GL_UNSIGNED_INT_SAMPLER_2D_ARRAY, "usampler2DArray" );
put( GL32.GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE, "usampler2DMS" );
put( GL32.GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY, "usampler2DMSArray" );
put( GL30.GL_UNSIGNED_INT_SAMPLER_BUFFER, "usamplerBuffer" );
put( GL30.GL_UNSIGNED_INT_SAMPLER_2D_RECT, "usampler2DRect" );
put( GL42.GL_IMAGE_1D, "image1D" );
put( GL42.GL_IMAGE_2D, "image2D" );
put( GL42.GL_IMAGE_3D, "image3D" );
put( GL42.GL_IMAGE_2D_RECT, "image2DRect" );
put( GL42.GL_IMAGE_CUBE, "imageCube" );
put( GL42.GL_IMAGE_BUFFER, "imageBuffer" );
put( GL42.GL_IMAGE_1D_ARRAY, "image1DArray" );
put( GL42.GL_IMAGE_2D_ARRAY, "image2DArray" );
put( GL42.GL_IMAGE_2D_MULTISAMPLE, "image2DMS" );
put( GL42.GL_IMAGE_2D_MULTISAMPLE_ARRAY, "image2DMSArray" );
put( GL42.GL_INT_IMAGE_1D, "iimage1D" );
put( GL42.GL_INT_IMAGE_2D, "iimage2D" );
put( GL42.GL_INT_IMAGE_3D, "iimage3D" );
put( GL42.GL_INT_IMAGE_2D_RECT, "iimage2DRect" );
put( GL42.GL_INT_IMAGE_CUBE, "iimageCube" );
put( GL42.GL_INT_IMAGE_BUFFER, "iimageBuffer" );
put( GL42.GL_INT_IMAGE_1D_ARRAY, "iimage1DArray" );
put( GL42.GL_INT_IMAGE_2D_ARRAY, "iimage2DArray" );
put( GL42.GL_INT_IMAGE_2D_MULTISAMPLE, "iimage2DMS" );
put( GL42.GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY, "iimage2DMSArray" );
put( GL42.GL_UNSIGNED_INT_IMAGE_1D, "uimage1D" );
put( GL42.GL_UNSIGNED_INT_IMAGE_2D, "uimage2D" );
put( GL42.GL_UNSIGNED_INT_IMAGE_3D, "uimage3D" );
put( GL42.GL_UNSIGNED_INT_IMAGE_2D_RECT, "uimage2DRect" );
put( GL42.GL_UNSIGNED_INT_IMAGE_CUBE, "uimageCube" );
put( GL42.GL_UNSIGNED_INT_IMAGE_BUFFER, "uimageBuffer" );
put( GL42.GL_UNSIGNED_INT_IMAGE_1D_ARRAY, "uimage1DArray" );
put( GL42.GL_UNSIGNED_INT_IMAGE_2D_ARRAY, "uimage2DArray" );
put( GL42.GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE, "uimage2DMS" );
put( GL42.GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY, "uimage2DMSArray" );
put( GL42.GL_UNSIGNED_INT_ATOMIC_COUNTER, "atomic_uint" );
}};
}
#version 110
in vec4 frag_color;
void main()
{
gl_FragColor = frag_color;
}
#version 330
layout(points) in;
layout(triangle_strip, max_vertices=24) out;
in vec4 v_color[];
in vec4 v_props[];
out vec4 frag_color;
const vec4 corner[] = vec4[](
vec4(-0.5, 0.5,-0.5, 0.0), // front top left
vec4( 0.5, 0.5,-0.5, 0.0), // front top right
vec4( 0.5,-0.5,-0.5, 0.0), // front bottom right
vec4(-0.5,-0.5,-0.5, 0.0), // front bottom left
vec4(-0.5, 0.5, 0.5, 0.0), // back top left
vec4( 0.5, 0.5, 0.5, 0.0), // back top right
vec4( 0.5,-0.5, 0.5, 0.0), // back bottom right
vec4(-0.5,-0.5, 0.5, 0.0) // back bottom left
);
mat4 eulerMatrix(vec3 angles)
{
float sinX = sin(angles.x);
float cosX = cos(angles.x);
float sinY = sin(angles.y);
float cosY = cos(angles.y);
float sinZ = sin(angles.z);
float cosZ = cos(angles.z);
return mat4( cosZ * cosY, cosZ * -sinY * -sinX + sinZ * cosX, cosZ * -sinY * cosX + sinZ * sinX, 0.0,
-sinZ * cosY,-sinZ * -sinY * -sinX + cosZ * cosX,-sinZ * -sinY * cosX + cosZ * sinX, 0.0,
sinY, cosY * -sinX, cosY * cosX, 0.0,
0.0, 0.0, 0.0, 1.0);
}
void main()
{
vec4 in_pos = gl_in[0].gl_Position;
vec4 in_color = v_color[0];
vec3 in_rotation = v_props[0].xyz;
float in_scale = v_props[0].w;
mat4 in_matrix = eulerMatrix( in_rotation );
vec4 corner0 = corner[0] * in_scale * in_matrix + in_pos;
vec4 corner1 = corner[1] * in_scale * in_matrix + in_pos;
vec4 corner2 = corner[2] * in_scale * in_matrix + in_pos;
vec4 corner3 = corner[3] * in_scale * in_matrix + in_pos;
vec4 corner4 = corner[4] * in_scale * in_matrix + in_pos;
vec4 corner5 = corner[5] * in_scale * in_matrix + in_pos;
vec4 corner6 = corner[6] * in_scale * in_matrix + in_pos;
vec4 corner7 = corner[7] * in_scale * in_matrix + in_pos;
// front face
frag_color = in_color;
gl_Position = corner0;
EmitVertex();
gl_Position = corner1;
EmitVertex();
gl_Position = corner3;
EmitVertex();
gl_Position = corner2;
EmitVertex();
EndPrimitive();
// back face
frag_color = vec4(in_color.rgb * 0.8, in_color.a);
gl_Position = corner5;
EmitVertex();
gl_Position = corner4;
EmitVertex();
gl_Position = corner6;
EmitVertex();
gl_Position = corner7;
EmitVertex();
EndPrimitive();
// left face
frag_color = vec4(in_color.rgb * 0.9, in_color.a);
gl_Position = corner4;
EmitVertex();
gl_Position = corner0;
EmitVertex();
gl_Position = corner7;
EmitVertex();
gl_Position = corner3;
EmitVertex();
EndPrimitive();
// right face
frag_color = vec4(in_color.rgb * 0.9, in_color.a);
gl_Position = corner1;
EmitVertex();
gl_Position = corner5;
EmitVertex();
gl_Position = corner2;
EmitVertex();
gl_Position = corner6;
EmitVertex();
EndPrimitive();
// top face
frag_color = vec4(in_color.rgb * 0.7, in_color.a);
gl_Position = corner4;
EmitVertex();
gl_Position = corner5;
EmitVertex();
gl_Position = corner0;
EmitVertex();
gl_Position = corner1;
EmitVertex();
EndPrimitive();
// bottom face
frag_color = vec4(in_color.rgb * 0.7, in_color.a);
gl_Position = corner3;
EmitVertex();
gl_Position = corner2;
EmitVertex();
gl_Position = corner7;
EmitVertex();
gl_Position = corner6;
EmitVertex();
EndPrimitive();
}
#version 330 compatibility
out vec4 v_color;
out vec4 v_props;
void main()
{
gl_Position = ftransform();
v_color = gl_Color;
v_props = gl_SecondaryColor;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment