View Full Version : How to use GLSL with JEDI-SDL?
cragwolf
15-04-2006, 01:12 AM
Does anyone know of an FPC or Delphi demo that shows how to use the OpenGL shading language with JEDI-SDL? Do I need the latest OpenGL headers? Are the ones that come with FPC or JEDI-SDL good enough?
The headers that come with FPC/Lazarus are for OpenGL 1.1, so they are definitely too old.
The headers from JEDI-SDL seem to be outdated too, I'm using the dglOpenGL (http://www.delphigl.com) headers, they should compile on Linux too, but I haven't tried it yet. At least I can compile my test project using the headers with FPC and it runs.
technomage
15-04-2006, 09:25 AM
The headers in the JEDI-SDL cvs are up to date and support OpenGL 2.0. These are being tested (by me). I have got a basic framework for GLSL I'll try and post it but you will need the headers from cvs for it to work.
cragwolf
15-04-2006, 11:31 AM
Thanks, I'd much appreciate that framework.
grudzio
18-04-2006, 11:53 AM
There is a tutorial at http://wiki.delphigl.com/index.php/Tutorial#Shader, but it is in german.
cragwolf
18-04-2006, 12:49 PM
Danke sch??n! 8)
I'm having trouble with the shaders... The shader loads just fine and I can set the uniform parameters, but I get "invalid operation" on glEnd();
Here's the vertex program:
void main(void)
{
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_Position = ftransform();
}
Fragment program:
uniform sampler2D colorMap;
void main(void)
{
gl_FragColor = texture2D( colorMap, gl_TexCoord[0].st);
}
Here's the test code:
glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, Textures[0]);
glCheckForError();
Shader.Enable;
glCheckForError();
Shader.SetParameter('colorMap', GL_TEXTURE0_ARB);
glCheckForError();
glBegin(GL_QUADS);
glColor3f(1, 1, 1);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 0);
glVertex3f(10, -10, -30);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 1);
glVertex3f(10, 10, -30);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 1);
glVertex3f(-10, 10, -30);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 0);
glVertex3f(-10, -10, -30);
glEnd();
glCheckForError(); // Error here
Shader.Disable;
The shader class is just a simple wrapper for loading the programs and setting parameters that I made for testing.
unit uShader;
{
Project Miniverse
http://www.projectminiverse.com
Description:
GLSL shader object
Classes:
TShader
Created:
22.04.2006 (JA)
Modified:
}
interface
uses
{$IFDEF USE_SDL}
GL, GLU, GLEXT,
{$ELSE}
dglOpenGL,
{$ENDIF}
glShader, glGeometry;
type
TShader = class(TObject)
protected
FProgramObject: GLHandleARB;
FName: String;
FResult: String;
public
constructor Create(FileName: String); overload;
constructor Create(const Name, VertexProgram, FragmentProgram: String); overload;
destructor Destroy; override;
procedure Enable;
procedure Disable;
procedure SetParameter(const Name: String; Value: TInt); overload;
procedure SetParameter(const Name: String; Value: TFloat); overload;
procedure SetParameter(const Name: String; Value1, Value2, Value3, Value4: TFloat); overload;
property Name: String read FName;
property ProgramObject: GLHandleARB read FProgramObject;
property Result: String read FResult;
end;
implementation
uses
glUtils;
constructor TShader.Create(FileName: String);
begin
inherited Create;
end;
constructor TShader.Create(const Name, VertexProgram, FragmentProgram: String);
begin
inherited Create;
FName := Name;
FProgramObject := glShader.LoadFragmentandVertexShader(FragmentProgr am, VertexProgram);
FResult := glShader.CheckForErrors(FProgramObject);
glCheckForError();
end;
destructor TShader.Destroy;
begin
inherited Destroy;
end;
procedure TShader.Enable;
begin
glUseProgramObjectARB(FProgramObject);
end;
procedure TShader.Disable;
begin
glUseProgramObjectARB(0);
end;
procedure TShader.SetParameter(const Name: String; Value: TInt);
var
p: PGLcharARB;
l: TInt;
begin
p := PGLCharArb(Name);
glUniform1iARB(glGetUniformLocationARB(FProgramObj ect, p), Value);
end;
procedure TShader.SetParameter(const Name: String; Value: TFloat);
var
p: PGLcharARB;
l: TInt;
begin
p := PGLCharARB(Name);
glUniform1fARB(glGetUniformLocationARB(FProgramObj ect, p), Value);
end;
procedure TShader.SetParameter(const Name: String; Value1, Value2, Value3, Value4: TFloat);
var
p: PGLcharARB;
l: TInt;
begin
p := PGLCharARB(Name);
glUniform4fARB(glGetUniformLocationARB(FProgramObj ect, p), Value1, Value2, Value3, Value4);
end;
end.
I tried to search for possible reason, but I can't seem to find any.
technomage
23-04-2006, 01:32 PM
Doesn't look like a problem with the Shader Programs. I tested the code in the ShaderDesigner and it works. I would first try to get the copde working without a shader (just use the fixed functionality) then add the shader once that is working. There might be a problem in the setup/initialization of the app which is causing the error.
It works if I use ARB multitexturing, but as soon as I try to use the shader I get the error.
I load the ARB extensions for multitexturing and shaders, ie. GL_ARB_multitexture and GL_ARB_shader_objects, do I need something else too? Do I need to initialize the OpenGL pipeline for shaders before using them? Doesn't seem likely as I can load and compile the shaders and the driver says: "Link successful. The GLSL vertex shader will run in hardware. The GLSL fragment shader will run in hardware."
Here's my OpenGL initialization code:
glClearColor(0.0, 0.0, 0.0, 0.0);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
glShadeModel(GL_SMOOTH);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_TEXTURE_2D);
Didn't find any documentation to explain what can I do and what I cannot do when using shaders...
technomage
23-04-2006, 03:23 PM
I'll have a look at something similar here and see if I can get it working.
technomage
23-04-2006, 03:51 PM
Works fine here.
Try changing
Shader.SetParameter('colorMap', GL_TEXTURE0_ARB);
to
Shader.SetParameter('colorMap', Textures[0]);
That might work, as I think it's the texture ID you need to pass in there not the opengl constant.
Umm... No?
Aren't you supposed to pass the id of the texture unit, that has the texture binded, to use as sampler2D object in the shader, not the texture itself?
I tried that too and it doesn't work any better...
Any chance that you could post some kind of simple example using the shaders ie. a complete program? So that I could compare it to my code and see what's the difference and where it goes wrong...
I'll try to make as simple program as possible using shaders and see if it still produces the error.
EDIT: Here's the program.
The shader class:
unit uShader;
{
Project Miniverse
http://www.projectminiverse.com
Description:
GLSL shader object
Classes:
TShader
Created:
22.04.2006 (JA)
Modified:
}
interface
uses
{$IFDEF USE_SDL}
GL, GLU, GLEXT,
{$ELSE}
dglOpenGL,
{$ENDIF}
glGeometry;
type
TShader = class(TObject)
protected
FProgramObject: GLHandleARB;
FName: String;
FResult: String;
public
constructor Create(FileName: String); overload;
constructor Create(const Name, VertexProgram, FragmentProgram: String); overload;
destructor Destroy; override;
function CheckForErrors(glObject: GLHandleARB): String;
function LoadFragmentandVertexShader(FFilename: string; VFilename: string): GLHandleARB;
procedure Enable;
procedure Disable;
procedure SetParameter(const Name: String; Value: TInt); overload;
procedure SetParameter(const Name: String; Value: TFloat); overload;
procedure SetParameter(const Name: String; Value1, Value2, Value3, Value4: TFloat); overload;
property Name: String read FName;
property ProgramObject: GLHandleARB read FProgramObject;
property Result: String read FResult;
end;
implementation
uses
Classes,
SysUtils,
glUtils;
constructor TShader.Create(FileName: String);
begin
inherited Create;
end;
constructor TShader.Create(const Name, VertexProgram, FragmentProgram: String);
begin
inherited Create;
FName := Name;
FProgramObject := LoadFragmentandVertexShader(FragmentProgram, VertexProgram);
FResult := CheckForErrors(FProgramObject);
glCheckForError();
end;
destructor TShader.Destroy;
begin
inherited Destroy;
end;
function TShader.CheckForErrors(glObject: GLHandleARB): String;
var
blen, slen: GLInt;
InfoLog : PGLCharARB;
begin
glGetObjectParameterivARB(glObject, GL_OBJECT_INFO_LOG_LENGTH_ARB, @blen);
if blen > 1 then
begin
GetMem(InfoLog, blen*SizeOf(GLCharARB));
{$IFDEF USE_SDL}
glGetInfoLogARB(glObject, blen , @slen, InfoLog);
{$ELSE}
glGetInfoLogARB(glObject, blen , slen, InfoLog);
{$ENDIF}
Result:= PChar(InfoLog);
Dispose(InfoLog);
end;
end;
function TShader.LoadFragmentandVertexShader(FFilename: string; VFilename: string): GLHandleARB;
var
ProgramObject, FragmentShaderObject, VertexShaderObject: GLHandleARB;
FShader: TStringList;
FShaderText: String;
FShaderLength: Integer;
VShader: TStringList;
VShaderText: String;
VShaderLength: Integer;
begin
Result := 0;
if FileExists(FFilename) then
if FileExists(VFilename) then
begin
ProgramObject := glCreateProgramObjectARB;
FragmentShaderObject := glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
VertexShaderObject := glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
FShader := TStringList.Create;
FShader.LoadFromFile(FFilename);
FShaderText := FShader.Text;
FShaderLength := Length(FShaderText);
VShader := TStringList.Create;
VShader.LoadFromFile(VFilename);
VShaderText := VShader.Text;
VShaderLength := Length(VShaderText);
glShaderSourceARB(VertexShaderObject, 1, @VShaderText, @VShaderLength);
glShaderSourceARB(FragmentShaderObject, 1, @FShaderText, @FShaderLength);
glCompileShaderARB(FragmentShaderObject);
glCompileShaderARB(VertexShaderObject);
glAttachObjectARB(ProgramObject, FragmentShaderObject);
glAttachObjectARB(ProgramObject, VertexShaderObject);
glDeleteObjectARB(FragmentShaderObject);
glDeleteObjectARB(VertexShaderObject);
glLinkProgramARB(ProgramObject);
Result := ProgramObject;
end;
end;
procedure TShader.Enable;
begin
glUseProgramObjectARB(FProgramObject);
end;
procedure TShader.Disable;
begin
glUseProgramObjectARB(0);
end;
procedure TShader.SetParameter(const Name: String; Value: TInt);
var
p: PGLcharARB;
begin
p := PGLCharArb(Name);
glUniform1iARB(glGetUniformLocationARB(FProgramObj ect, p), Value);
end;
procedure TShader.SetParameter(const Name: String; Value: TFloat);
var
p: PGLcharARB;
begin
p := PGLCharARB(Name);
glUniform1fARB(glGetUniformLocationARB(FProgramObj ect, p), Value);
end;
procedure TShader.SetParameter(const Name: String; Value1, Value2, Value3, Value4: TFloat);
var
p: PGLcharARB;
begin
p := PGLCharARB(Name);
glUniform4fARB(glGetUniformLocationARB(FProgramObj ect, p), Value1, Value2, Value3, Value4);
end;
end.
And the test program:
program SDL_OpenGLTest;
uses
SysUtils,
GL, GLU, GLEXT,
SDL,
Windows,
uShader,
uTextureManager;
const
// screen width, height, and bit depth
SCREEN_WIDTH = 640;
SCREEN_HEIGHT = 480;
SCREEN_BPP = 32;
MAX_EXTENSION = 4;
Extensions: array [0..MAX_EXTENSION] of String = (
'GL_ARB_multitexture',
'GL_ARB_texture_cube_map',
'GL_ARB_fragment_shader',
'GL_ARB_vertex_shader',
'GL_ARB_shader_objects'
);
type
EOpenGLError = class(Exception);
var
// This is our SDL surface
surface : PSDL_Surface;
Ready: Boolean;
Shader: TShader;
Textures: array [0..1] of GLuInt;
procedure glCheckForError;
var
glError: GLenum;
begin
glError := glGetError();
if (glError <> GL_NO_ERROR) then
raise EOpenGLError.Create('OpenGL error! Code: ' + IntToStr(glError) + ' ($' + IntToHex(glError, 4) + ' hex) Description: ' + gluErrorString(glError));
end;
procedure TerminateApplication;
begin
SDL_QUIT;
Halt(0);
end;
procedure HandleError(ErrClass: String; ErrException: Exception; ErrMethod: String);
begin
Ready := FALSE;
MessageBox(0, PChar('Class: ' + ErrClass + #13#10 + 'Method: ' + ErrMethod + #13#10 + 'Exception: ' + ErrException.ClassName + #13#10 + 'Message: ' + ErrException.Message), 'Error!', MB_ICONERROR or MB_OK);
TerminateApplication;
end;
// function to reset our viewport after a window resize
function ResizeWindow( width : integer; height : integer ) : Boolean;
var
h: Integer;
begin
try
// Protect against a divide by zero
if (Height = 0) then
h := 1
else
h := Height;
// Setup our viewport.
glViewport( 0, 0, Width, h );
// Change to the projection matrix and set our viewing volume.
glMatrixMode( GL_PROJECTION );
glLoadIdentity;
// Set our perspective
gluPerspective( 45.0, Width / h, 0.1, 1000.0 );
// Make sure we're changing the model view and not the projection
glMatrixMode( GL_MODELVIEW );
// Reset The View
glLoadIdentity();
glEnable(GL_TEXTURE_2D);
Result := TRUE;
except
raise;
end;
end;
// function to handle key press events
procedure HandleKeyPress( keysym : PSDL_keysym );
begin
case keysym^.sym of
SDLK_ESCAPE :
// ESC key was pressed
TerminateApplication;
end;
end;
// A general OpenGL initialization function. Sets all of the initial parameters.
procedure InitGL;
// We call this right after our OpenGL window is created.
var
s: String;
i: Integer;
begin
gTextureManager := TTextureManager.Create;
if not glext_LoadExtension('GL_version_1_2') then
s := 'OpenGL version 1.2 not supported!' + #13#10;
// Load extensions
for i := 0 to MAX_EXTENSION do
if glext_ExtensionSupported(PChar(Extensions[i]), #0) then
glext_LoadExtension(Extensions[i])
else
s := Extensions[i] + #13#10;
if s <> '' then
raise EOpenGLError('All needed OpenGL extensions are not supported! Unsupported extensions: ' + #13#10 + s);
glClearColor(0.0, 0.0, 0.0, 0.0);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
glShadeModel(GL_SMOOTH);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_TEXTURE_2D);
glCheckForError();
Textures[0] := gTextureManager.GetTexture('Data\Textures\Misc\Che cker.bmp');
Textures[1] := gTextureManager.GetTexture('Data\Textures\Misc\Ope nGL.bmp');
Shader := TShader.Create('SimpleShader', 'Shaders\texture.vp', 'Shaders\texture.fp');
glCheckForError();
ResizeWindow(SCREEN_WIDTH, SCREEN_HEIGHT);
Ready := TRUE;
end;
// The main drawing function.
procedure DrawGLScene;
begin
if not Ready then
Exit;
try
glClear( GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT or GL_STENCIL_BUFFER_BIT );
glLoadIdentity();
glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, Textures[0]);
//glActiveTextureARB(GL_TEXTURE1_ARB);
//glEnable(GL_TEXTURE_2D);
//glBindTexture(GL_TEXTURE_2D, Textures[1]);
//glCheckForError();
Shader.Enable;
glCheckForError();
Shader.SetParameter('colorMap', GL_TEXTURE0_ARB);
glCheckForError();
glColor3f(1, 1, 1);
glBegin(GL_QUADS);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 0);
//glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 0);
glVertex3f(10, -10, -30);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 1);
//glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 1);
glVertex3f(10, 10, -30);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 1);
//glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 1);
glVertex3f(-10, 10, -30);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 0);
//glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 0);
glVertex3f(-10, -10, -30);
glEnd();
glCheckForError();
Shader.Disable;
// swap buffers to display, since we're double buffered.
SDL_GL_SwapBuffers;
except
on E: Exception do
HandleError('Application', E, 'DrawGLScene');
end;
end;
var
Done : Boolean;
event : TSDL_Event;
videoflags : Uint32;
videoInfo : PSDL_VideoInfo;
begin
// Initialize SDL
if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) then
begin
TerminateApplication;
end;
// Fetch the video info
videoInfo := SDL_GetVideoInfo;
if ( videoInfo = nil ) then
begin
TerminateApplication;
end;
// the flags to pass to SDL_SetVideoMode
videoFlags := SDL_OPENGL; // Enable OpenGL in SDL
videoFlags := videoFlags or SDL_DOUBLEBUF; // Enable double buffering
videoFlags := videoFlags or SDL_HWPALETTE; // Store the palette in hardware
// This checks to see if surfaces can be stored in memory
if ( videoInfo^.hw_available <> 0 ) then
videoFlags := videoFlags or SDL_HWSURFACE
else
videoFlags := videoFlags or SDL_SWSURFACE;
// This checks if hardware blits can be done * /
if ( videoInfo^.blit_hw <> 0 ) then
videoFlags := videoFlags or SDL_HWACCEL;
// Set the OpenGL Attributes
SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
// Set the title bar in environments that support it
SDL_WM_SetCaption( 'SDL OpenGL test', nil );
videoflags := videoFlags or SDL_RESIZABLE; // Enable window resizing
//videoflags := videoFlags or SDL_FULLSCREEN; // Enable window resizing
surface := SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, videoflags );
if ( surface = nil ) then
begin
TerminateApplication;
end;
InitGL;
// Loop, drawing and checking events
Done := FALSE;
while ( not Done ) do
begin
// This could go in a separate function
while ( SDL_PollEvent( @event ) = 1 ) do
begin
case event.type_ of
SDL_QUITEV :
begin
Done := true;
end;
SDL_KEYDOWN :
begin
// handle key presses
HandleKeyPress( @event.key.keysym );
end;
SDL_VIDEORESIZE :
begin
surface := SDL_SetVideoMode( event.resize.w, event.resize.h, SCREEN_BPP, videoflags );
if ( surface = nil ) then
begin
TerminateApplication;
end;
ResizeWindow( event.resize.w, event.resize.h );
end;
end;
end;
// draw the scene
DrawGLScene;
end;
TerminateApplication;
end.
The TextureManager class loads/saves/caches the textures, you can replace it with something similar (it would be too big to post here).
Any ideas why it doesn't work?
grudzio
23-04-2006, 09:39 PM
The texture units are indexed from 0 to n -1 where n is the number of avaiable texture units.
So to use first unit send 0 instad of GL_TEXTURE0_ARB and everything should be okay.
Yes! That's it! Now it works!
Thanks a lot. :)
Powered by vBulletin® Version 4.2.5 Copyright © 2024 vBulletin Solutions Inc. All rights reserved.