gltf_viewer_lib/
shader.rs

1use std::collections::HashMap;
2use std::ffi::CString;
3use std::fs::File;
4use std::io::Read;
5use std::ptr;
6use std::str;
7
8use gl;
9use gl::types::*;
10
11use cgmath::{Matrix, Matrix4, Vector3, Vector4};
12use cgmath::prelude::*;
13
14use bitflags::bitflags;
15use log::{warn, trace};
16
17pub struct Shader {
18    pub id: u32,
19    uniform_location_cache: HashMap<&'static str, i32>
20}
21
22impl Shader {
23    #[allow(dead_code)]
24    pub fn new(vertex_path: &str, fragment_path: &str, defines: &[String]) -> Shader {
25        // 1. retrieve the vertex/fragment source code from filesystem
26        let mut v_shader_file = File::open(vertex_path).unwrap_or_else(|_| panic!("Failed to open {}", vertex_path));
27        let mut f_shader_file = File::open(fragment_path).unwrap_or_else(|_| panic!("Failed to open {}", fragment_path));
28        let mut vertex_code = String::new();
29        let mut fragment_code = String::new();
30        v_shader_file
31            .read_to_string(&mut vertex_code)
32            .expect("Failed to read vertex shader");
33        f_shader_file
34            .read_to_string(&mut fragment_code)
35            .expect("Failed to read fragment shader");
36
37        Self::from_source(&vertex_code, &fragment_code, defines)
38    }
39
40    pub fn from_source(vertex_code: &str, fragment_code: &str, defines: &[String]) -> Shader {
41        let mut shader = Shader {
42            id: 0,
43            uniform_location_cache: HashMap::new()
44        };
45
46        let vertex_code = Self::add_defines(vertex_code, defines);
47        let v_shader_code = CString::new(vertex_code.as_bytes()).unwrap();
48        let fragment_code = Self::add_defines(fragment_code, defines);
49        let f_shader_code = CString::new(fragment_code.as_bytes()).unwrap();
50
51        // 2. compile shaders
52        unsafe {
53            // vertex shader
54            let vertex = gl::CreateShader(gl::VERTEX_SHADER);
55            gl::ShaderSource(vertex, 1, &v_shader_code.as_ptr(), ptr::null());
56            gl::CompileShader(vertex);
57            shader.check_compile_errors(vertex, "VERTEX");
58            // fragment Shader
59            let fragment = gl::CreateShader(gl::FRAGMENT_SHADER);
60            gl::ShaderSource(fragment, 1, &f_shader_code.as_ptr(), ptr::null());
61            gl::CompileShader(fragment);
62            shader.check_compile_errors(fragment, "FRAGMENT");
63            // shader Program
64            let id = gl::CreateProgram();
65            gl::AttachShader(id, vertex);
66            gl::AttachShader(id, fragment);
67            gl::LinkProgram(id);
68            shader.check_compile_errors(id, "PROGRAM");
69            // delete the shaders as they're linked into our program now and no longer necessary
70            gl::DeleteShader(vertex);
71            gl::DeleteShader(fragment);
72            shader.id = id;
73        }
74
75        shader
76    }
77
78    fn add_defines(source: &str, defines: &[String]) -> String {
79        // insert preprocessor defines after #version if exists
80        // (#version must occur before any other statement in the program)
81        let defines = defines.iter()
82            .map(|define| format!("#define {}", define))
83            .collect::<Vec<_>>()
84            .join("\n");
85        let mut lines: Vec<_> = source.lines().collect();
86        if let Some(version_line) = lines.iter().position(|l| l.starts_with("#version")) {
87            lines.insert(version_line+1, &defines);
88        }
89        else {
90            lines.insert(0, &defines);
91        }
92        lines.join("\n")
93    }
94
95    /// activate the shader
96    /// ------------------------------------------------------------------------
97    pub unsafe fn use_program(&self) {
98        gl::UseProgram(self.id)
99    }
100
101    /// utility uniform functions
102    /// ------------------------------------------------------------------------
103    #[allow(dead_code)]
104    pub unsafe fn set_bool(&self, location: i32, value: bool) {
105        gl::Uniform1i(location, value as i32);
106    }
107    /// ------------------------------------------------------------------------
108    pub unsafe fn set_int(&self, location: i32, value: i32) {
109        gl::Uniform1i(location, value);
110    }
111    /// ------------------------------------------------------------------------
112    pub unsafe fn set_float(&self, location: i32, value: f32) {
113        gl::Uniform1f(location, value);
114    }
115    /// ------------------------------------------------------------------------
116    pub unsafe fn set_vector3(&self, location: i32, value: &Vector3<f32>) {
117        gl::Uniform3fv(location, 1, value.as_ptr());
118    }
119    /// ------------------------------------------------------------------------
120    pub unsafe fn set_vector4(&self, location: i32, value: &Vector4<f32>) {
121        gl::Uniform4fv(location, 1, value.as_ptr());
122    }
123    /// ------------------------------------------------------------------------
124    pub unsafe fn set_vec2(&self, location: i32, x: f32, y: f32) {
125        gl::Uniform2f(location, x, y);
126    }
127    /// ------------------------------------------------------------------------
128    pub unsafe fn set_vec3(&self, location: i32, x: f32, y: f32, z: f32) {
129        gl::Uniform3f(location, x, y, z);
130    }
131    /// ------------------------------------------------------------------------
132    pub unsafe fn set_mat4(&self, location: i32, mat: &Matrix4<f32>) {
133        gl::UniformMatrix4fv(location, 1, gl::FALSE, mat.as_ptr());
134    }
135
136    /// get uniform location with caching
137    pub unsafe fn uniform_location(&mut self, name: &'static str) -> i32 {
138        if let Some(loc) = self.uniform_location_cache.get(name) {
139            return *loc;
140        }
141
142        let c_name = CString::new(name).unwrap();
143        let loc = gl::GetUniformLocation(self.id, c_name.as_ptr());
144        if loc == -1 {
145            trace!("uniform '{}' unknown for shader {}", name, self.id);
146        }
147        self.uniform_location_cache.insert(name, loc);
148        loc
149    }
150
151    /// utility function for checking shader compilation/linking errors.
152    /// ------------------------------------------------------------------------
153    unsafe fn check_compile_errors(&self, shader: u32, type_: &str) {
154        let mut success = i32::from(gl::FALSE);
155        let mut info_log = Vec::with_capacity(1024);
156        info_log.set_len(1024 - 1); // subtract 1 to skip the trailing null character
157        if type_ != "PROGRAM" {
158            gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut success);
159            let log_type = if success == i32::from(gl::TRUE) { "WARNING" } else { "ERROR" };
160            let mut length = 0;
161            gl::GetShaderInfoLog(shader, 1024, &mut length, info_log.as_mut_ptr() as *mut GLchar);
162            if length == 0 { return }
163            panic!("{}::SHADER_COMPILATION_{} of type: {}\n{}",
164                      log_type, log_type,
165                      type_,
166                      str::from_utf8(&info_log[0..length as usize]).unwrap());
167
168        } else {
169            gl::GetProgramiv(shader, gl::LINK_STATUS, &mut success);
170            let log_type = if success == i32::from(gl::TRUE) { "WARNING" } else { "ERROR" };
171            let mut length = 0;
172            gl::GetProgramInfoLog(shader, 1024, &mut length, info_log.as_mut_ptr() as *mut GLchar);
173            if length == 0 { return }
174            warn!("{}::PROGRAM_LINKING_{} of type: {}\n{}",
175                      log_type, log_type,
176                      type_,
177                      str::from_utf8(&info_log[0..length as usize]).unwrap());
178        }
179
180    }
181}
182
183bitflags! {
184    /// Flags matching the defines in the PBR shader
185    pub struct ShaderFlags: u16 {
186        // vertex shader + fragment shader
187        const HAS_NORMALS           = 1;
188        const HAS_TANGENTS          = 1 << 1;
189        const HAS_UV                = 1 << 2;
190        const HAS_COLORS            = 1 << 3;
191
192        // fragment shader only
193        const USE_IBL               = 1 << 4;
194        const HAS_BASECOLORMAP      = 1 << 5;
195        const HAS_NORMALMAP         = 1 << 6;
196        const HAS_EMISSIVEMAP       = 1 << 7;
197        const HAS_METALROUGHNESSMAP = 1 << 8;
198        const HAS_OCCLUSIONMAP      = 1 << 9;
199        const USE_TEX_LOD           = 1 << 10;
200    }
201}
202
203impl ShaderFlags {
204    pub fn as_strings(self) -> Vec<String> {
205        (0..15)
206            .map(|i| 1u16 << i)
207            .filter(|i| self.bits & i != 0)
208            .map(|i| format!("{:?}", ShaderFlags::from_bits_truncate(i)))
209            .collect()
210    }
211}
212
213#[allow(non_snake_case)]
214pub struct PbrUniformLocations {
215    // uniform locations
216    // TODO!: UBO for matrices, camera, light(s)?
217    pub u_MVPMatrix: i32,
218    pub u_ModelMatrix: i32,
219    pub u_Camera: i32,
220
221    pub u_LightDirection: i32,
222    pub u_LightColor: i32,
223
224    pub u_AmbientLightColor: i32,
225    pub u_AmbientLightIntensity: i32,
226
227    // TODO!: set when integrating IBL (unused now)
228    pub u_DiffuseEnvSampler: i32,
229    pub u_SpecularEnvSampler: i32,
230    pub u_brdfLUT: i32,
231
232    ///
233
234    pub u_BaseColorSampler: i32,
235    pub u_BaseColorTexCoord: i32,
236    pub u_BaseColorFactor: i32,
237
238    pub u_NormalSampler: i32,
239    pub u_NormalTexCoord: i32,
240    pub u_NormalScale: i32,
241
242    pub u_EmissiveSampler: i32,
243    pub u_EmissiveTexCoord: i32,
244    pub u_EmissiveFactor: i32,
245
246    pub u_MetallicRoughnessSampler: i32,
247    pub u_MetallicRoughnessTexCoord: i32,
248    pub u_MetallicRoughnessValues: i32,
249
250    pub u_OcclusionSampler: i32,
251    pub u_OcclusionTexCoord: i32,
252    pub u_OcclusionStrength: i32,
253
254    pub u_AlphaBlend: i32,
255    pub u_AlphaCutoff: i32,
256
257    // TODO!: use/remove debugging uniforms
258    // debugging flags used for shader output of intermediate PBR variables
259    pub u_ScaleDiffBaseMR: i32,
260    pub u_ScaleFGDSpec: i32,
261    pub u_ScaleIBLAmbient: i32,
262}
263
264pub struct PbrShader {
265    pub shader: Shader,
266    pub flags: ShaderFlags,
267    pub uniforms: PbrUniformLocations,
268}
269
270impl PbrShader {
271    pub fn new(flags: ShaderFlags) -> Self {
272        let mut shader = Shader::from_source(
273            include_str!("shaders/pbr-vert.glsl"),
274            include_str!("shaders/pbr-frag.glsl"),
275            &flags.as_strings());
276
277        // NOTE: shader debug version
278        // let mut shader = Shader::new(
279        //     "src/shaders/pbr-vert.glsl",
280        //     "src/shaders/pbr-frag.glsl",
281        //     &flags.as_strings());
282
283        let uniforms = unsafe {
284            let uniforms = PbrUniformLocations {
285                u_MVPMatrix: shader.uniform_location("u_MVPMatrix"),
286                u_ModelMatrix: shader.uniform_location("u_ModelMatrix"),
287                u_Camera: shader.uniform_location("u_Camera"),
288
289                u_LightDirection: shader.uniform_location("u_LightDirection"),
290                u_LightColor: shader.uniform_location("u_LightColor"),
291
292                u_AmbientLightColor: shader.uniform_location("u_AmbientLightColor"),
293                u_AmbientLightIntensity: shader.uniform_location("u_AmbientLightIntensity"),
294
295                u_DiffuseEnvSampler: shader.uniform_location("u_DiffuseEnvSampler"),
296                u_SpecularEnvSampler: shader.uniform_location("u_SpecularEnvSampler"),
297                u_brdfLUT: shader.uniform_location("u_brdfLUT"),
298
299                u_BaseColorSampler: shader.uniform_location("u_BaseColorSampler"),
300                u_BaseColorTexCoord: shader.uniform_location("u_BaseColorTexCoord"),
301                u_BaseColorFactor: shader.uniform_location("u_BaseColorFactor"),
302
303                u_NormalSampler: shader.uniform_location("u_NormalSampler"),
304                u_NormalTexCoord: shader.uniform_location("u_NormalTexCoord"),
305                u_NormalScale: shader.uniform_location("u_NormalScale"),
306
307                u_EmissiveSampler: shader.uniform_location("u_EmissiveSampler"),
308                u_EmissiveTexCoord: shader.uniform_location("u_EmissiveTexCoord"),
309                u_EmissiveFactor: shader.uniform_location("u_EmissiveFactor"),
310
311                u_MetallicRoughnessSampler: shader.uniform_location("u_MetallicRoughnessSampler"),
312                u_MetallicRoughnessTexCoord: shader.uniform_location("u_MetallicRoughnessTexCoord"),
313                u_MetallicRoughnessValues: shader.uniform_location("u_MetallicRoughnessValues"),
314
315                u_OcclusionSampler: shader.uniform_location("u_OcclusionSampler"),
316                u_OcclusionTexCoord: shader.uniform_location("u_OcclusionTexCoord"),
317                u_OcclusionStrength: shader.uniform_location("u_OcclusionStrength"),
318
319                u_AlphaBlend: shader.uniform_location("u_AlphaBlend"),
320                u_AlphaCutoff: shader.uniform_location("u_AlphaCutoff"),
321
322                u_ScaleDiffBaseMR: shader.uniform_location("u_ScaleDiffBaseMR"),
323                u_ScaleFGDSpec: shader.uniform_location("u_ScaleFGDSpec"),
324                u_ScaleIBLAmbient: shader.uniform_location("u_ScaleIBLAmbient"),
325            };
326
327            shader.use_program();
328            shader.set_int(uniforms.u_BaseColorSampler, 0);
329            shader.set_int(uniforms.u_NormalSampler, 1);
330            shader.set_int(uniforms.u_EmissiveSampler, 2);
331            shader.set_int(uniforms.u_MetallicRoughnessSampler, 3);
332            shader.set_int(uniforms.u_OcclusionSampler, 4);
333
334            shader.set_vec3(uniforms.u_LightColor, 5.0, 5.0, 5.0);
335            // TODO!: optional minus on z
336            shader.set_vec3(uniforms.u_LightDirection, 0.0, 0.5, 0.5);
337
338            shader.set_vec3(uniforms.u_AmbientLightColor, 1.0, 1.0, 1.0);
339            shader.set_float(uniforms.u_AmbientLightIntensity, 0.2);
340
341            uniforms
342        };
343
344        Self {
345            shader,
346            flags,
347            uniforms
348        }
349    }
350}