1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
|
/*
This file is part of the MinGfx Project.
Copyright (c) 2017,2018 Regents of the University of Minnesota.
All Rights Reserved.
Original Author(s) of this File:
Dan Keefe, 2018, University of Minnesota
Author(s) of Significant Updates/Modifications to the File:
...
*/
#ifndef SRC_DEFAULT_SHADER_H_
#define SRC_DEFAULT_SHADER_H_
#include "color.h"
#include "point3.h"
#include "shader_program.h"
#include "texture2d.h"
#include "vector3.h"
#include "matrix4.h"
#include "mesh.h"
namespace mingfx {
/** A simple GLSL shader for textured per-fragment Phong shading with multiple
light sources. This can be used to draw 3D models stored in a mingfx::Mesh
data structure or you can use it with your own geometry data structures.
Lighting properties are stored within the class itself since these are considered
part of the shading model. Material properties are considered properties of the
meshes or other materials you wish to draw so these are stored outside of the
class and passed into the Draw() or UseProgram() functions.
An example of using DefaultShader to render a mesh:
~~~
DefaultShader phong_shader;
Mesh teapot;
DefaultShader::MaterialProperties teapot_material;
void Init() {
// initialize the shader
DefaultShader::LightProperties red_light;
red_light.position = Point3(-10, 5, 5);
red_light.diffuseIntensity = Color(1,0,0);
phong_shader.AddLight(red_light);
// initialize the mesh
teapot.LoadFromOBJ(Platform::FindMinGfxDataFile("teapot.obj"));
}
void DrawUsingOpenGL() {
Matrix4 M;
Matrix4 V = Matrix4::LookAt(Point3(0,0,3), Point3(0,0,0), Vector3(0,1,0));
Matrix4 P = Matrix4::Perspective(60.0, aspect_ratio(), 0.1, 10.0);
phong_shader.Draw(M, V, P, teapot, teapot_material);
}
~~~
*/
class DefaultShader {
public:
/// If changed, this needs to also be changed in the glsl shader code
static const unsigned int MAX_LIGHTS = 10;
/// Small data structure to hold properties of the material to be lit
class MaterialProperties {
public:
Color ambient_reflectance;
Color diffuse_reflectance;
Color specular_reflectance;
float shinniness;
Texture2D surface_texture;
// eventually, this might include a normal map, etc.
// defaults
MaterialProperties() :
ambient_reflectance(0.25f, 0.25f, 0.25f),
diffuse_reflectance(0.6f, 0.6f, 0.6f),
specular_reflectance(0.4f, 0.4f, 0.4f),
shinniness(20.0f) {}
};
/// Small data structure to hold per-light properties
class LightProperties {
public:
Point3 position;
Color ambient_intensity;
Color diffuse_intensity;
Color specular_intensity;
// defaults
LightProperties() :
position(10.0f, 10.0f, 10.0f),
ambient_intensity(0.25f, 0.25f, 0.25f),
diffuse_intensity(0.6f, 0.6f, 0.6f),
specular_intensity(0.6f, 0.6f, 0.6f) {}
};
/// The constructor defaults to adding a single white light to the scene at
/// (10,10,10). Change this by passing it 'false'. The constructor does
/// not load and compile the shader right away. This is done inside Init().
DefaultShader(bool add_default_light=true);
virtual ~DefaultShader();
/// Multiple lights are supported, this adds one to the end of the list.
/// Up to MAX_LIGHTS can be added.
void AddLight(LightProperties light);
/// Changes the properties for a light that was already added.
void SetLight(int i, LightProperties light);
/// This loads vertex and fragment shaders from files, compiles them, and
/// links them. So, it must be called from within an active OpenGL context,
/// for example, from within GraphicsApp::Init() or GraphicsApp::DrawUsingOpenGL().
/// If you call Draw() before calling Init(), then Init() will be called as
/// the first step within Draw(). So, if you do not mind a slowdown on the
/// very first frame of your program, it is fine to skip calling Init().
void Init();
/// This starts the shader and sets its uniform variables based upon the
/// current set of lights, the material properties passed in, and the
/// model, view, and projection matrices. Then, it calls mesh->Draw().
/// After drawing, it disables the shader.
void Draw(const Matrix4 &model, const Matrix4 &view, const Matrix4 &projection,
Mesh *mesh, const MaterialProperties &material);
/// Only needed if you do not want to draw a Mesh.
/// This does all of the same setup for drawing that the Draw() function does
/// and then it returns so that you may draw your own geometry however you want.
/// After doing your draw must call StopProgram() to turn off the shader.
void UseProgram(const Matrix4 &model, const Matrix4 &view, const Matrix4 &projection,
const MaterialProperties &material);
/// Only needed if you do not want to draw a Mesh. Call this after UseProgram()
/// and after drawing your geometry to turn off the shader.
void StopProgram();
int num_lights();
LightProperties light(int i);
private:
std::vector<LightProperties> lights_;
// cached raw float arrays store data to send directly to the gpu
// GLSL requires fixed size arrays for these
float lightPositions_[3*MAX_LIGHTS];
float lightIas_[4*MAX_LIGHTS];
float lightIds_[4*MAX_LIGHTS];
float lightIss_[4*MAX_LIGHTS];
void update_light_arrays();
ShaderProgram phongShader_;
};
} // end namespace
#endif
|