Initial commit
- Deferred renderer (forward) - Loading models (temp) - Started towards differentiation
This commit is contained in:
commit
7054c965a4
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
*.bin filter=lfs diff=lfs merge=lfs -text
|
||||
*.png filter=lfs diff=lfs merge=lfs -text
|
||||
*.pdf filter=lfs diff=lfs merge=lfs -text
|
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
*.dll
|
||||
*.so
|
||||
*.o
|
||||
*.zip
|
||||
*.tar.gz
|
||||
build/
|
3
README.md
Normal file
3
README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# PROBLEM STATEMENT
|
||||
|
||||
Differentiable Renderer that Supports One (1) Non-Obvious Camera Model
|
4
glad/README.md
Normal file
4
glad/README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
Generated from: https://gen.glad.sh
|
||||
Check include/glad/gl.h for license information.
|
||||
Includes khrplatform.h (C) The Khronos Group Inc.
|
||||
|
311
glad/include/KHR/khrplatform.h
Normal file
311
glad/include/KHR/khrplatform.h
Normal file
|
@ -0,0 +1,311 @@
|
|||
#ifndef __khrplatform_h_
|
||||
#define __khrplatform_h_
|
||||
|
||||
/*
|
||||
** Copyright (c) 2008-2018 The Khronos Group Inc.
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a
|
||||
** copy of this software and/or associated documentation files (the
|
||||
** "Materials"), to deal in the Materials without restriction, including
|
||||
** without limitation the rights to use, copy, modify, merge, publish,
|
||||
** distribute, sublicense, and/or sell copies of the Materials, and to
|
||||
** permit persons to whom the Materials are furnished to do so, subject to
|
||||
** the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included
|
||||
** in all copies or substantial portions of the Materials.
|
||||
**
|
||||
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
*/
|
||||
|
||||
/* Khronos platform-specific types and definitions.
|
||||
*
|
||||
* The master copy of khrplatform.h is maintained in the Khronos EGL
|
||||
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
|
||||
* The last semantic modification to khrplatform.h was at commit ID:
|
||||
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
|
||||
*
|
||||
* Adopters may modify this file to suit their platform. Adopters are
|
||||
* encouraged to submit platform specific modifications to the Khronos
|
||||
* group so that they can be included in future versions of this file.
|
||||
* Please submit changes by filing pull requests or issues on
|
||||
* the EGL Registry repository linked above.
|
||||
*
|
||||
*
|
||||
* See the Implementer's Guidelines for information about where this file
|
||||
* should be located on your system and for more details of its use:
|
||||
* http://www.khronos.org/registry/implementers_guide.pdf
|
||||
*
|
||||
* This file should be included as
|
||||
* #include <KHR/khrplatform.h>
|
||||
* by Khronos client API header files that use its types and defines.
|
||||
*
|
||||
* The types in khrplatform.h should only be used to define API-specific types.
|
||||
*
|
||||
* Types defined in khrplatform.h:
|
||||
* khronos_int8_t signed 8 bit
|
||||
* khronos_uint8_t unsigned 8 bit
|
||||
* khronos_int16_t signed 16 bit
|
||||
* khronos_uint16_t unsigned 16 bit
|
||||
* khronos_int32_t signed 32 bit
|
||||
* khronos_uint32_t unsigned 32 bit
|
||||
* khronos_int64_t signed 64 bit
|
||||
* khronos_uint64_t unsigned 64 bit
|
||||
* khronos_intptr_t signed same number of bits as a pointer
|
||||
* khronos_uintptr_t unsigned same number of bits as a pointer
|
||||
* khronos_ssize_t signed size
|
||||
* khronos_usize_t unsigned size
|
||||
* khronos_float_t signed 32 bit floating point
|
||||
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
|
||||
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
|
||||
* nanoseconds
|
||||
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
|
||||
* khronos_boolean_enum_t enumerated boolean type. This should
|
||||
* only be used as a base type when a client API's boolean type is
|
||||
* an enum. Client APIs which use an integer or other type for
|
||||
* booleans cannot use this as the base type for their boolean.
|
||||
*
|
||||
* Tokens defined in khrplatform.h:
|
||||
*
|
||||
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
|
||||
*
|
||||
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
|
||||
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
|
||||
*
|
||||
* Calling convention macros defined in this file:
|
||||
* KHRONOS_APICALL
|
||||
* KHRONOS_APIENTRY
|
||||
* KHRONOS_APIATTRIBUTES
|
||||
*
|
||||
* These may be used in function prototypes as:
|
||||
*
|
||||
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
|
||||
* int arg1,
|
||||
* int arg2) KHRONOS_APIATTRIBUTES;
|
||||
*/
|
||||
|
||||
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
|
||||
# define KHRONOS_STATIC 1
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APICALL
|
||||
*-------------------------------------------------------------------------
|
||||
* This precedes the return type of the function in the function prototype.
|
||||
*/
|
||||
#if defined(KHRONOS_STATIC)
|
||||
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
|
||||
* header compatible with static linking. */
|
||||
# define KHRONOS_APICALL
|
||||
#elif defined(_WIN32)
|
||||
# define KHRONOS_APICALL __declspec(dllimport)
|
||||
#elif defined (__SYMBIAN32__)
|
||||
# define KHRONOS_APICALL IMPORT_C
|
||||
#elif defined(__ANDROID__)
|
||||
# define KHRONOS_APICALL __attribute__((visibility("default")))
|
||||
#else
|
||||
# define KHRONOS_APICALL
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIENTRY
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the return type of the function and precedes the function
|
||||
* name in the function prototype.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
|
||||
/* Win32 but not WinCE */
|
||||
# define KHRONOS_APIENTRY __stdcall
|
||||
#else
|
||||
# define KHRONOS_APIENTRY
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIATTRIBUTES
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the closing parenthesis of the function prototype arguments.
|
||||
*/
|
||||
#if defined (__ARMCC_2__)
|
||||
#define KHRONOS_APIATTRIBUTES __softfp
|
||||
#else
|
||||
#define KHRONOS_APIATTRIBUTES
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* basic type definitions
|
||||
*-----------------------------------------------------------------------*/
|
||||
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
|
||||
|
||||
|
||||
/*
|
||||
* Using <stdint.h>
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
/*
|
||||
* To support platform where unsigned long cannot be used interchangeably with
|
||||
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
|
||||
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
|
||||
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
|
||||
* unsigned long long or similar (this results in different C++ name mangling).
|
||||
* To avoid changes for existing platforms, we restrict usage of intptr_t to
|
||||
* platforms where the size of a pointer is larger than the size of long.
|
||||
*/
|
||||
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
|
||||
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
|
||||
#define KHRONOS_USE_INTPTR_T
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#elif defined(__VMS ) || defined(__sgi)
|
||||
|
||||
/*
|
||||
* Using <inttypes.h>
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
||||
|
||||
/*
|
||||
* Win32
|
||||
*/
|
||||
typedef __int32 khronos_int32_t;
|
||||
typedef unsigned __int32 khronos_uint32_t;
|
||||
typedef __int64 khronos_int64_t;
|
||||
typedef unsigned __int64 khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif defined(__sun__) || defined(__digital__)
|
||||
|
||||
/*
|
||||
* Sun or Digital
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#if defined(__arch64__) || defined(_LP64)
|
||||
typedef long int khronos_int64_t;
|
||||
typedef unsigned long int khronos_uint64_t;
|
||||
#else
|
||||
typedef long long int khronos_int64_t;
|
||||
typedef unsigned long long int khronos_uint64_t;
|
||||
#endif /* __arch64__ */
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#elif 0
|
||||
|
||||
/*
|
||||
* Hypothetical platform with no float or int64 support
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#define KHRONOS_SUPPORT_INT64 0
|
||||
#define KHRONOS_SUPPORT_FLOAT 0
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Generic fallback
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
typedef int64_t khronos_int64_t;
|
||||
typedef uint64_t khronos_uint64_t;
|
||||
#define KHRONOS_SUPPORT_INT64 1
|
||||
#define KHRONOS_SUPPORT_FLOAT 1
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Types that are (so far) the same on all platforms
|
||||
*/
|
||||
typedef signed char khronos_int8_t;
|
||||
typedef unsigned char khronos_uint8_t;
|
||||
typedef signed short int khronos_int16_t;
|
||||
typedef unsigned short int khronos_uint16_t;
|
||||
|
||||
/*
|
||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
||||
* to be the only LLP64 architecture in current use.
|
||||
*/
|
||||
#ifdef KHRONOS_USE_INTPTR_T
|
||||
typedef intptr_t khronos_intptr_t;
|
||||
typedef uintptr_t khronos_uintptr_t;
|
||||
#elif defined(_WIN64)
|
||||
typedef signed long long int khronos_intptr_t;
|
||||
typedef unsigned long long int khronos_uintptr_t;
|
||||
#else
|
||||
typedef signed long int khronos_intptr_t;
|
||||
typedef unsigned long int khronos_uintptr_t;
|
||||
#endif
|
||||
|
||||
#if defined(_WIN64)
|
||||
typedef signed long long int khronos_ssize_t;
|
||||
typedef unsigned long long int khronos_usize_t;
|
||||
#else
|
||||
typedef signed long int khronos_ssize_t;
|
||||
typedef unsigned long int khronos_usize_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_FLOAT
|
||||
/*
|
||||
* Float type
|
||||
*/
|
||||
typedef float khronos_float_t;
|
||||
#endif
|
||||
|
||||
#if KHRONOS_SUPPORT_INT64
|
||||
/* Time types
|
||||
*
|
||||
* These types can be used to represent a time interval in nanoseconds or
|
||||
* an absolute Unadjusted System Time. Unadjusted System Time is the number
|
||||
* of nanoseconds since some arbitrary system event (e.g. since the last
|
||||
* time the system booted). The Unadjusted System Time is an unsigned
|
||||
* 64 bit value that wraps back to 0 every 584 years. Time intervals
|
||||
* may be either signed or unsigned.
|
||||
*/
|
||||
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
|
||||
typedef khronos_int64_t khronos_stime_nanoseconds_t;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Dummy value used to pad enum types to 32 bits.
|
||||
*/
|
||||
#ifndef KHRONOS_MAX_ENUM
|
||||
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enumerated boolean type
|
||||
*
|
||||
* Values other than zero should be considered to be true. Therefore
|
||||
* comparisons should not be made against KHRONOS_TRUE.
|
||||
*/
|
||||
typedef enum {
|
||||
KHRONOS_FALSE = 0,
|
||||
KHRONOS_TRUE = 1,
|
||||
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
|
||||
} khronos_boolean_enum_t;
|
||||
|
||||
#endif /* __khrplatform_h_ */
|
3637
glad/include/glad/gl.h
Normal file
3637
glad/include/glad/gl.h
Normal file
File diff suppressed because it is too large
Load Diff
1620
glad/src/gl.c
Normal file
1620
glad/src/gl.c
Normal file
File diff suppressed because it is too large
Load Diff
32
meson.build
Normal file
32
meson.build
Normal file
|
@ -0,0 +1,32 @@
|
|||
project('diffren', 'c',
|
||||
default_options: 'warning_level=3')
|
||||
|
||||
cmake = import('cmake')
|
||||
|
||||
glfw_dep = dependency('glfw3', required: true)
|
||||
|
||||
assimp_opts = cmake.subproject_options()
|
||||
assimp_subproj = cmake.subproject('assimp-5.3.1')
|
||||
assimp_dep = assimp_subproj.dependency('assimp')
|
||||
|
||||
incdirs = include_directories('glad' / 'include')
|
||||
|
||||
glad_lib = static_library('glad',
|
||||
'glad/src/gl.c',
|
||||
override_options: ['warning_level=0'],
|
||||
include_directories: incdirs)
|
||||
|
||||
executable('diffren',
|
||||
'src/diffren.h',
|
||||
'src/diffren_math.h',
|
||||
'src/diffren_model.h',
|
||||
'src/main.c',
|
||||
'src/deferred.c',
|
||||
'src/model_loader.c',
|
||||
'src/math.c',
|
||||
|
||||
'src/stb_image.h',
|
||||
dependencies: [glfw_dep, assimp_dep],
|
||||
include_directories: incdirs,
|
||||
override_options: ['c_std=c11'],
|
||||
link_with: glad_lib)
|
BIN
model/WaterBottle.bin
(Stored with Git LFS)
Normal file
BIN
model/WaterBottle.bin
(Stored with Git LFS)
Normal file
Binary file not shown.
172
model/WaterBottle.gltf
Normal file
172
model/WaterBottle.gltf
Normal file
|
@ -0,0 +1,172 @@
|
|||
{
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 0,
|
||||
"componentType": 5126,
|
||||
"count": 2549,
|
||||
"type": "VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"componentType": 5126,
|
||||
"count": 2549,
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 2,
|
||||
"componentType": 5126,
|
||||
"count": 2549,
|
||||
"type": "VEC4"
|
||||
},
|
||||
{
|
||||
"bufferView": 3,
|
||||
"componentType": 5126,
|
||||
"count": 2549,
|
||||
"type": "VEC3",
|
||||
"max": [
|
||||
0.05445001,
|
||||
0.130220339,
|
||||
0.0544500239
|
||||
],
|
||||
"min": [
|
||||
-0.05445001,
|
||||
-0.130220339,
|
||||
-0.0544500239
|
||||
]
|
||||
},
|
||||
{
|
||||
"bufferView": 4,
|
||||
"componentType": 5123,
|
||||
"count": 13530,
|
||||
"type": "SCALAR"
|
||||
}
|
||||
],
|
||||
"asset": {
|
||||
"generator": "glTF Tools for Unity",
|
||||
"version": "2.0"
|
||||
},
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 20392
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 20392,
|
||||
"byteLength": 30588
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 50980,
|
||||
"byteLength": 40784
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 91764,
|
||||
"byteLength": 30588
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteOffset": 122352,
|
||||
"byteLength": 27060
|
||||
}
|
||||
],
|
||||
"buffers": [
|
||||
{
|
||||
"uri": "WaterBottle.bin",
|
||||
"byteLength": 149412
|
||||
}
|
||||
],
|
||||
"images": [
|
||||
{
|
||||
"uri": "WaterBottle_baseColor.png"
|
||||
},
|
||||
{
|
||||
"uri": "WaterBottle_occlusionRoughnessMetallic.png"
|
||||
},
|
||||
{
|
||||
"uri": "WaterBottle_normal.png"
|
||||
},
|
||||
{
|
||||
"uri": "WaterBottle_emissive.png"
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"TEXCOORD_0": 0,
|
||||
"NORMAL": 1,
|
||||
"TANGENT": 2,
|
||||
"POSITION": 3
|
||||
},
|
||||
"indices": 4,
|
||||
"material": 0
|
||||
}
|
||||
],
|
||||
"name": "WaterBottle"
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"pbrMetallicRoughness": {
|
||||
"baseColorTexture": {
|
||||
"index": 0
|
||||
},
|
||||
"metallicRoughnessTexture": {
|
||||
"index": 1
|
||||
}
|
||||
},
|
||||
"normalTexture": {
|
||||
"index": 2
|
||||
},
|
||||
"occlusionTexture": {
|
||||
"index": 1
|
||||
},
|
||||
"emissiveFactor": [
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"emissiveTexture": {
|
||||
"index": 3
|
||||
},
|
||||
"name": "BottleMat"
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"mesh": 0,
|
||||
"rotation": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"name": "WaterBottle"
|
||||
}
|
||||
],
|
||||
"scene": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"nodes": [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"textures": [
|
||||
{
|
||||
"source": 0
|
||||
},
|
||||
{
|
||||
"source": 1
|
||||
},
|
||||
{
|
||||
"source": 2
|
||||
},
|
||||
{
|
||||
"source": 3
|
||||
}
|
||||
]
|
||||
}
|
BIN
model/WaterBottle_baseColor.png
(Stored with Git LFS)
Normal file
BIN
model/WaterBottle_baseColor.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
model/WaterBottle_emissive.png
(Stored with Git LFS)
Normal file
BIN
model/WaterBottle_emissive.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
model/WaterBottle_normal.png
(Stored with Git LFS)
Normal file
BIN
model/WaterBottle_normal.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
model/WaterBottle_occlusionRoughnessMetallic.png
(Stored with Git LFS)
Normal file
BIN
model/WaterBottle_occlusionRoughnessMetallic.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
papers/3D_EDGE_DETECTION_BASED_ON_NORMAL_VECTORS.pdf
(Stored with Git LFS)
Normal file
BIN
papers/3D_EDGE_DETECTION_BASED_ON_NORMAL_VECTORS.pdf
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
papers/Differentiable_Rendering_A_Survey.pdf
(Stored with Git LFS)
Normal file
BIN
papers/Differentiable_Rendering_A_Survey.pdf
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
papers/OpenDR.pdf
(Stored with Git LFS)
Normal file
BIN
papers/OpenDR.pdf
(Stored with Git LFS)
Normal file
Binary file not shown.
31
shader/geom_frag.glsl
Normal file
31
shader/geom_frag.glsl
Normal file
|
@ -0,0 +1,31 @@
|
|||
#version 460 core
|
||||
|
||||
// Note: orm = occlusion/roughness/metallic
|
||||
|
||||
layout (location = 0) out vec3 g_position;
|
||||
layout (location = 1) out vec3 g_albedo;
|
||||
layout (location = 2) out vec3 g_normal;
|
||||
layout (location = 3) out vec3 g_orm;
|
||||
|
||||
in VS_OUT {
|
||||
vec3 viewPos;
|
||||
vec2 texcoord;
|
||||
mat3 TBN;
|
||||
} fs_in;
|
||||
|
||||
layout (location = 1) uniform sampler2D tex_albedo;
|
||||
layout (location = 2) uniform sampler2D tex_normal;
|
||||
layout (location = 3) uniform sampler2D tex_orm;
|
||||
|
||||
void main() {
|
||||
// Just sample albedo and occlusion/roughness/metallic
|
||||
g_albedo = texture(tex_albedo, fs_in.texcoord).rgb;
|
||||
g_orm = texture(tex_orm, fs_in.texcoord).rgb;
|
||||
|
||||
g_position = fs_in.viewPos;
|
||||
|
||||
// Normal mapping
|
||||
vec3 normal = texture(tex_normal, fs_in.texcoord).rgb;
|
||||
normal = normal * 2.0 - 1.0;
|
||||
g_normal = normalize(fs_in.TBN * normal);
|
||||
}
|
34
shader/geom_vert.glsl
Normal file
34
shader/geom_vert.glsl
Normal file
|
@ -0,0 +1,34 @@
|
|||
#version 460 core
|
||||
|
||||
layout (location = 0) in vec3 v_pos;
|
||||
layout (location = 1) in vec3 v_normal;
|
||||
layout (location = 2) in vec3 v_tangent;
|
||||
layout (location = 3) in vec2 v_texcoord;
|
||||
|
||||
out VS_OUT {
|
||||
vec3 viewPos;
|
||||
vec2 texcoord;
|
||||
mat3 TBN;
|
||||
} vs_out;
|
||||
|
||||
layout (std140, binding=0) uniform FrameMatrices {
|
||||
mat4 view;
|
||||
mat4 projection;
|
||||
};
|
||||
layout (location=0) uniform mat4 model;
|
||||
|
||||
void main() {
|
||||
gl_Position = projection * view * model * vec4(v_pos, 1.0);
|
||||
|
||||
vs_out.texcoord = v_texcoord;
|
||||
|
||||
// Gram-schmidt process for re-orthogonalizing the TBN vectors
|
||||
vec3 T = normalize(vec3(view * model * vec4(v_tangent, 0.0)));
|
||||
vec3 N = normalize(vec3(view * model * vec4(v_normal, 0.0)));
|
||||
T = normalize(T - dot(T, N) * N);
|
||||
vec3 B = cross(N, T);
|
||||
vs_out.TBN = mat3(T, B, N);
|
||||
|
||||
vs_out.viewPos = (view * model * vec4(v_pos, 1.0)).xyz;
|
||||
}
|
||||
|
123
shader/light_frag.glsl
Normal file
123
shader/light_frag.glsl
Normal file
|
@ -0,0 +1,123 @@
|
|||
#version 460 core
|
||||
|
||||
// hdr color
|
||||
layout (location = 0) out vec3 h_color;
|
||||
|
||||
struct Light {
|
||||
// w contains the radius
|
||||
vec4 pos_radius;
|
||||
vec4 color;
|
||||
};
|
||||
|
||||
layout(std430, binding = 0) buffer LightBuffer
|
||||
{
|
||||
Light lights[];
|
||||
};
|
||||
layout (location = 0) uniform uint light_count;
|
||||
layout (location = 1) uniform vec3 ambient_color;
|
||||
|
||||
// GBuffer
|
||||
layout (location = 2) uniform sampler2D g_position;
|
||||
layout (location = 3) uniform sampler2D g_albedo;
|
||||
layout (location = 4) uniform sampler2D g_normal;
|
||||
layout (location = 5) uniform sampler2D g_orm;
|
||||
|
||||
struct Surface {
|
||||
vec3 position;
|
||||
vec3 albedo;
|
||||
vec3 normal;
|
||||
float occlusion;
|
||||
float roughness;
|
||||
float metallic;
|
||||
};
|
||||
|
||||
in VS_OUT {
|
||||
vec2 texcoord;
|
||||
} fs_in;
|
||||
|
||||
const float PI = 3.1415926;
|
||||
|
||||
float GGXDistribution(float alpha, vec3 normal, vec3 H) {
|
||||
float a2 = alpha * alpha;
|
||||
float NdotH = dot(normal, H);
|
||||
float NdotH2 = NdotH * NdotH;
|
||||
float num = a2 * max(NdotH, 0.0); // both are length = 1, so this is heaviside
|
||||
float den = (NdotH2 * (a2 - 1.0) + 1);
|
||||
return num / (PI * den * den);
|
||||
}
|
||||
|
||||
float SmithJoinMaskingShadowing(float alpha, vec3 normal, vec3 light_dir, vec3 view_dir, vec3 H) {
|
||||
float NdotL = abs(dot(normal, light_dir));
|
||||
float NdotV = abs(dot(normal, view_dir));
|
||||
float NdotL2 = NdotL * NdotL;
|
||||
float NdotV2 = NdotV * NdotV;
|
||||
float alpha2 = alpha * alpha;
|
||||
|
||||
float num1 = 2 * NdotL * max(dot(H, light_dir), 0);
|
||||
float den1 = NdotL + sqrt(alpha2 + (1-alpha2)*NdotL2);
|
||||
float num2 = 2 * NdotV * max(dot(H, view_dir), 0);
|
||||
float den2 = NdotV + sqrt(alpha2 + (1-alpha2)*NdotV2);
|
||||
|
||||
return (num1*num2)/(den1*den2);
|
||||
}
|
||||
|
||||
vec3 FresnelSlick(vec3 f0, vec3 view_dir, vec3 H) {
|
||||
float VdotH = dot(view_dir, H);
|
||||
return f0 + (1.0 - f0) * pow(1 - abs(VdotH), 5);
|
||||
}
|
||||
|
||||
vec3 Lerp(vec3 a, vec3 b, float t) {
|
||||
return a + (b - a) * t;
|
||||
}
|
||||
|
||||
Surface ReadSurface() {
|
||||
Surface surf;
|
||||
surf.position = texture(g_position, fs_in.texcoord).rgb;
|
||||
surf.albedo = texture(g_albedo, fs_in.texcoord).rgb;
|
||||
surf.normal = texture(g_normal, fs_in.texcoord).rgb;
|
||||
vec3 orm = texture(g_orm, fs_in.texcoord).rgb;
|
||||
surf.occlusion = orm.r;
|
||||
surf.roughness = orm.g;
|
||||
surf.metallic = orm.b;
|
||||
return surf;
|
||||
}
|
||||
|
||||
void main() {
|
||||
Surface surf = ReadSurface();
|
||||
// Eye location is always (0,0,0) because we calculate everything in view-space
|
||||
vec3 view_dir = normalize(-1 * surf.position);
|
||||
vec3 black = vec3(0, 0, 0);
|
||||
|
||||
vec3 c_diff = Lerp(surf.albedo, black, surf.metallic);
|
||||
vec3 f0 = Lerp(vec3(0.04), surf.albedo, surf.metallic);
|
||||
|
||||
// We probably want a tiling based approach, but this is good enough for now
|
||||
vec3 Lo = vec3(0.0);
|
||||
for (uint i = 0; i < light_count; ++i) {
|
||||
vec3 light_pos = lights[i].pos_radius.xyz;
|
||||
float light_radius = lights[i].pos_radius.w;
|
||||
float light_dist = length(light_pos - surf.position);
|
||||
if (light_dist > light_radius)
|
||||
continue;
|
||||
float attenuation = 1.0 / (light_dist * light_dist);
|
||||
vec3 radiance = lights[i].color.rgb * attenuation;
|
||||
|
||||
vec3 light_dir = normalize(-1 * light_pos);
|
||||
vec3 H = normalize(light_dir + view_dir);
|
||||
|
||||
float NdotL = dot(surf.normal, light_dir);
|
||||
|
||||
vec3 F = FresnelSlick(f0, view_dir, H);
|
||||
|
||||
vec3 f_diffuse = (1 - F) * (1 / PI) * c_diff;
|
||||
vec3 f_specular = F * GGXDistribution(surf.roughness, surf.normal, H)
|
||||
* SmithJoinMaskingShadowing(surf.roughness, surf.normal, light_dir, view_dir, H)
|
||||
/ (4 * abs(dot(view_dir, surf.normal)) * abs(NdotL) + 0.0001);
|
||||
|
||||
vec3 material = f_diffuse + f_specular;
|
||||
Lo += material * radiance * NdotL;
|
||||
}
|
||||
|
||||
vec3 ambient = ambient_color * surf.albedo * surf.occlusion;
|
||||
h_color = ambient + Lo;
|
||||
}
|
16
shader/light_vert.glsl
Normal file
16
shader/light_vert.glsl
Normal file
|
@ -0,0 +1,16 @@
|
|||
#version 460 core
|
||||
|
||||
out VS_OUT {
|
||||
vec2 texcoord;
|
||||
} vs_out;
|
||||
|
||||
void main() {
|
||||
// Output a single full-screen triangle
|
||||
vec2 verts[3] = vec2[3](
|
||||
vec2(-1,-1),
|
||||
vec2( 3,-1),
|
||||
vec2(-1, 3)
|
||||
);
|
||||
gl_Position = vec4(verts[gl_VertexID], 0, 1);
|
||||
vs_out.texcoord = 0.5 * gl_Position.xy + vec2(0.5);
|
||||
}
|
48
shader/pixel_classifier.glsl
Normal file
48
shader/pixel_classifier.glsl
Normal file
|
@ -0,0 +1,48 @@
|
|||
#version 460 core
|
||||
|
||||
// Compute shader for classifying pixels as either:
|
||||
// edge or internal
|
||||
|
||||
layout(local_size_x = 16, local_size_y = 16) in;
|
||||
|
||||
layout(rgba16f, binding = 0) uniform restrict readonly image2D g_normal;
|
||||
|
||||
layout(r8ui, binding = 1) uniform restrict writeonly uimage2D classification;
|
||||
|
||||
const uint INTERNAL = 0;
|
||||
const uint EDGE = 1;
|
||||
|
||||
// approx cos(10°)
|
||||
const float THRESH = 0.984;
|
||||
|
||||
void main() {
|
||||
ivec2 img_coord = ivec2(gl_GlobalInvocationID.xy);
|
||||
ivec2 img_size = imageSize(g_normal);
|
||||
|
||||
vec3 normals[9] = vec3[9](
|
||||
vec3(0, 0, 0), vec3(0, 0, 0), vec3(0, 0, 0),
|
||||
vec3(0, 0, 0), vec3(0, 0, 0), vec3(0, 0, 0),
|
||||
vec3(0, 0, 0), vec3(0, 0, 0), vec3(0, 0, 0)
|
||||
);
|
||||
int i = 0;
|
||||
for (int dy = -1; dy <= 1; ++dy) {
|
||||
int y = img_coord.y + dy;
|
||||
if (y < 0 || y == img_size.y)
|
||||
continue;
|
||||
for (int dx = -1; dx <= 1; ++dx) {
|
||||
int x = img_coord.x + dx;
|
||||
if (x < 0 || x == img_size.x)
|
||||
continue;
|
||||
normals[i] = imageLoad(g_normal, ivec2(x, y)).xyz;
|
||||
}
|
||||
}
|
||||
|
||||
uint classif = INTERNAL;
|
||||
for (int i = 0; i < 9; ++i) {
|
||||
float angle = abs(dot(normals[i], normals[4]));
|
||||
if (angle >= THRESH)
|
||||
classif = EDGE;
|
||||
}
|
||||
|
||||
imageStore(classification, img_coord, uvec4(classif));
|
||||
}
|
15
shader/tone_map_frag.glsl
Normal file
15
shader/tone_map_frag.glsl
Normal file
|
@ -0,0 +1,15 @@
|
|||
#version 460
|
||||
|
||||
layout (location = 0) out vec4 frag_color;
|
||||
|
||||
layout (location = 0) uniform sampler2D h_color;
|
||||
|
||||
in VS_OUT {
|
||||
vec2 texcoord;
|
||||
} fs_in;
|
||||
|
||||
void main() {
|
||||
vec3 hdr = texture(h_color, fs_in.texcoord).rgb;
|
||||
vec3 ldr = hdr / (hdr + vec3(1.0));
|
||||
frag_color = vec4(pow(ldr, vec3(1.0/2.2)), 1.0);
|
||||
}
|
16
shader/tone_map_vert.glsl
Normal file
16
shader/tone_map_vert.glsl
Normal file
|
@ -0,0 +1,16 @@
|
|||
#version 460 core
|
||||
|
||||
out VS_OUT {
|
||||
vec2 texcoord;
|
||||
} vs_out;
|
||||
|
||||
void main() {
|
||||
// Output a single full-screen triangle
|
||||
vec2 verts[3] = vec2[3](
|
||||
vec2(-1,-1),
|
||||
vec2( 3,-1),
|
||||
vec2(-1, 3)
|
||||
);
|
||||
gl_Position = vec4(verts[gl_VertexID], 0, 1);
|
||||
vs_out.texcoord = 0.5 * gl_Position.xy + vec2(0.5);
|
||||
}
|
700
src/deferred.c
Normal file
700
src/deferred.c
Normal file
|
@ -0,0 +1,700 @@
|
|||
#include <glad/gl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "diffren.h"
|
||||
#include "diffren_model.h"
|
||||
|
||||
/* Implements a deferred renderer for PBR models */
|
||||
|
||||
typedef struct {
|
||||
/* 16bit RGB float for view space positions */
|
||||
GLuint position;
|
||||
|
||||
/* RGB8 Buffer for surface base color */
|
||||
GLuint albedo;
|
||||
|
||||
/* 16bit float RGBs for view space normals */
|
||||
GLuint normal;
|
||||
|
||||
/* 16bit float RGB for occlusion, roughness and metallic */
|
||||
GLuint orm;
|
||||
|
||||
/* D32 for depth */
|
||||
GLuint depth;
|
||||
|
||||
/* Framebuffer object for the whole gbuffer */
|
||||
GLuint fbo;
|
||||
} GBuffer;
|
||||
|
||||
// Must match gl std140 layout
|
||||
typedef struct {
|
||||
M4 view;
|
||||
M4 projection;
|
||||
} FrameMatrices;
|
||||
|
||||
#define GEOMETRY_PASS_FRAME_MATRICES_BINDING 0
|
||||
#define GEOMETRY_PASS_MODEL_LOCATION 0
|
||||
#define GEOMETRY_PASS_ALBEDO_LOCATION 1
|
||||
#define GEOMETRY_PASS_NORMAL_LOCATION 2
|
||||
#define GEOMETRY_PASS_ORM_LOCATION 3
|
||||
|
||||
typedef struct {
|
||||
/* Shader program for rendering to the gbuffer */
|
||||
GLuint prog;
|
||||
|
||||
/* Buffer containing the view/projection matrices,
|
||||
* at binding = 0 */
|
||||
GLuint frame_matrices_ubo;
|
||||
} GeometryPass;
|
||||
|
||||
typedef struct {
|
||||
/* 16bit RGB float for HDR lighting computations */
|
||||
GLuint color;
|
||||
|
||||
/* Framebuffer object */
|
||||
GLuint fbo;
|
||||
} HDRBuffer;
|
||||
|
||||
#define LIGHTING_PASS_LIGHTS_BINDING 0
|
||||
#define LIGHTING_PASS_LIGHT_COUNT_LOCATION 0
|
||||
#define LIGHTING_PASS_AMBIENT_COLOR_LOCATION 1
|
||||
#define LIGHTING_PASS_POSITION_LOCATION 2
|
||||
#define LIGHTING_PASS_ALBEDO_LOCATION 3
|
||||
#define LIGHTING_PASS_NORMAL_LOCATION 4
|
||||
#define LIGHTING_PASS_ORM_LOCATION 5
|
||||
|
||||
typedef struct {
|
||||
/* Shader program for rendering the final image */
|
||||
GLuint prog;
|
||||
|
||||
GLuint lights_sbo;
|
||||
} LightingPass;
|
||||
|
||||
#define TONE_MAPPING_PASS_COLOR_LOCATION 0
|
||||
|
||||
typedef struct {
|
||||
GLuint prog;
|
||||
} ToneMappingPass;
|
||||
|
||||
#define PIXEL_CLASSIFIER_PASS_NORMAL_BINDING 0
|
||||
#define PIXEL_CLASSIFIER_PASS_CLASSIFICATION_BINDING 1
|
||||
|
||||
typedef struct {
|
||||
GLuint prog;
|
||||
} PixelClassifierPass;
|
||||
|
||||
typedef struct {
|
||||
// UI8 buffer for pixel classification
|
||||
GLuint classification;
|
||||
} DifferentiationBuffer;
|
||||
|
||||
#define MAX_LIGHTS 128
|
||||
|
||||
// Must match gl std430 layout
|
||||
typedef struct {
|
||||
V4 position_radius;
|
||||
V4 color;
|
||||
} Light;
|
||||
|
||||
struct DeferredRenderer {
|
||||
GBuffer gbuf;
|
||||
GeometryPass geometry_pass;
|
||||
|
||||
HDRBuffer hdr_buf;
|
||||
LightingPass lighting_pass;
|
||||
ToneMappingPass tone_mapping_pass;
|
||||
|
||||
PixelClassifierPass classifier_pass;
|
||||
DifferentiationBuffer diff_buffer;
|
||||
|
||||
/* "Empty" vao for the full-screen triangle */
|
||||
GLuint full_screen_triangle_vao;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
|
||||
/* Per Frame Data */
|
||||
V3 eye_pos;
|
||||
V3 center;
|
||||
V3 up;
|
||||
M4 projection;
|
||||
V3 ambient_color;
|
||||
|
||||
Light lights[MAX_LIGHTS];
|
||||
unsigned int light_count;
|
||||
int light_ssbo_needs_update;
|
||||
};
|
||||
|
||||
static void CreateGBuffer(int w, int h, GBuffer *gbuf) {
|
||||
glGenFramebuffers(1, &gbuf->fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, gbuf->fbo);
|
||||
|
||||
/* Create the texture objects in one batch */
|
||||
GLuint tex_objs[4];
|
||||
glGenTextures(4, tex_objs);
|
||||
gbuf->position = tex_objs[0];
|
||||
gbuf->albedo = tex_objs[1];
|
||||
gbuf->normal = tex_objs[2];
|
||||
gbuf->orm = tex_objs[3];
|
||||
|
||||
/* position buffer */
|
||||
glBindTexture(GL_TEXTURE_2D, gbuf->position);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, w, h, 0, GL_RGB, GL_FLOAT, NULL);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gbuf->position, 0);
|
||||
|
||||
/* albedo buffer */
|
||||
glBindTexture(GL_TEXTURE_2D, gbuf->albedo);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, w, h, 0, GL_RGB, GL_FLOAT, NULL);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, gbuf->albedo, 0);
|
||||
|
||||
/* normal buffer */
|
||||
glBindTexture(GL_TEXTURE_2D, gbuf->normal);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, w, h, 0, GL_RGB, GL_FLOAT, NULL);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, gbuf->normal, 0);
|
||||
|
||||
/* occlusion/roughness/metallic buffer */
|
||||
glBindTexture(GL_TEXTURE_2D, gbuf->orm);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, w, h, 0, GL_RGB, GL_FLOAT, NULL);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, gbuf->orm, 0);
|
||||
|
||||
/* 32bit depth buffer */
|
||||
glGenRenderbuffers(1, &gbuf->depth);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, gbuf->depth);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32F, w, h);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, gbuf->depth);
|
||||
|
||||
GLuint attachments[4] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 };
|
||||
glDrawBuffers(4, attachments);
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
fprintf(stderr, "ERROR::FRAMEBUFFER:: Framebuffer is not complete!\n");
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
/* Return a zero terminated string containing the shader code */
|
||||
static char *LoadShader(const char *path) {
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) {
|
||||
fprintf(stderr, "Failed to load %s: Could not fopen()\n", path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
size_t fsz = (size_t)ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
char *buf = malloc(fsz + 1);
|
||||
if (!buf) {
|
||||
fprintf(stderr, "Failed to load %s: Unable to allocate %zu bytes.\n", path, fsz + 1);
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
if (fread(buf, fsz, 1, f) != 1) {
|
||||
fprintf(stderr, "Failed to load %s: Unable to read %zu bytes.\n", path, fsz);
|
||||
free(buf);
|
||||
fclose(f);
|
||||
return NULL;
|
||||
}
|
||||
buf[fsz] = '\0';
|
||||
fclose(f);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int CreateGeometryPass(GeometryPass *pass) {
|
||||
/* Load vertex & fragment shader */
|
||||
char *vert_code = LoadShader("shader/geom_vert.glsl");
|
||||
if (!vert_code)
|
||||
return 0;
|
||||
char *frag_code = LoadShader("shader/geom_frag.glsl");
|
||||
if (!frag_code) {
|
||||
free(vert_code);
|
||||
return 0;
|
||||
}
|
||||
|
||||
GLint result;
|
||||
GLuint vert = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(vert, 1, &vert_code, NULL);
|
||||
glCompileShader(vert);
|
||||
glGetShaderiv(vert, GL_COMPILE_STATUS, &result);
|
||||
if (result != GL_TRUE) {
|
||||
char info[512];
|
||||
glGetShaderInfoLog(vert, sizeof(info), NULL, info);
|
||||
fprintf(stderr, "Failed to compile shader/geom_vert.glsl: %s\n", info);
|
||||
free(vert_code);
|
||||
free(frag_code);
|
||||
return 0;
|
||||
}
|
||||
|
||||
GLuint frag = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(frag, 1, &frag_code, NULL);
|
||||
glCompileShader(frag);
|
||||
glGetShaderiv(frag, GL_COMPILE_STATUS, &result);
|
||||
if (result != GL_TRUE) {
|
||||
char info[512];
|
||||
glGetShaderInfoLog(frag, sizeof(info), NULL, info);
|
||||
fprintf(stderr, "Failed to compile shader/geom_frag.glsl: %s\n", info);
|
||||
free(vert_code);
|
||||
free(frag_code);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pass->prog = glCreateProgram();
|
||||
glAttachShader(pass->prog, vert);
|
||||
glAttachShader(pass->prog, frag);
|
||||
glLinkProgram(pass->prog);
|
||||
glGetProgramiv(pass->prog, GL_LINK_STATUS, &result);
|
||||
if (result != GL_TRUE) {
|
||||
char info[512];
|
||||
glGetProgramInfoLog(pass->prog, sizeof(info), NULL, info);
|
||||
fprintf(stderr, "Failed to link geometry pass program: %s\n", info);
|
||||
free(vert_code);
|
||||
free(frag_code);
|
||||
return 0;
|
||||
}
|
||||
glDeleteShader(vert);
|
||||
glDeleteShader(frag);
|
||||
free(vert_code);
|
||||
free(frag_code);
|
||||
|
||||
glGenBuffers(1, &pass->frame_matrices_ubo);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, pass->frame_matrices_ubo);
|
||||
glBufferData(GL_UNIFORM_BUFFER, sizeof(FrameMatrices), NULL, GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void CreateHDRBuffer(int w, int h, HDRBuffer *hdr_buf) {
|
||||
glGenFramebuffers(1, &hdr_buf->fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, hdr_buf->fbo);
|
||||
|
||||
/* Create the texture objects in one batch */
|
||||
glGenTextures(1, &hdr_buf->color);
|
||||
|
||||
/* position buffer */
|
||||
glBindTexture(GL_TEXTURE_2D, hdr_buf->color);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, w, h, 0, GL_RGB, GL_FLOAT, NULL);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, hdr_buf->color, 0);
|
||||
|
||||
GLuint attachments[1] = { GL_COLOR_ATTACHMENT0 };
|
||||
glDrawBuffers(1, attachments);
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
fprintf(stderr, "ERROR::FRAMEBUFFER:: Framebuffer is not complete!\n");
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
static int CreateLightingPass(LightingPass *pass) {
|
||||
/* Load vertex & fragment shader */
|
||||
char *vert_code = LoadShader("shader/light_vert.glsl");
|
||||
if (!vert_code)
|
||||
return 0;
|
||||
char *frag_code = LoadShader("shader/light_frag.glsl");
|
||||
if (!frag_code) {
|
||||
free(vert_code);
|
||||
return 0;
|
||||
}
|
||||
|
||||
GLint result;
|
||||
GLuint vert = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(vert, 1, &vert_code, NULL);
|
||||
glCompileShader(vert);
|
||||
glGetShaderiv(vert, GL_COMPILE_STATUS, &result);
|
||||
if (result != GL_TRUE) {
|
||||
char info[512];
|
||||
glGetShaderInfoLog(vert, sizeof(info), NULL, info);
|
||||
fprintf(stderr, "Failed to compile shader/light_vert.glsl: %s\n", info);
|
||||
free(vert_code);
|
||||
free(frag_code);
|
||||
return 0;
|
||||
}
|
||||
|
||||
GLuint frag = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(frag, 1, &frag_code, NULL);
|
||||
glCompileShader(frag);
|
||||
glGetShaderiv(frag, GL_COMPILE_STATUS, &result);
|
||||
if (result != GL_TRUE) {
|
||||
char info[512];
|
||||
glGetShaderInfoLog(frag, sizeof(info), NULL, info);
|
||||
fprintf(stderr, "Failed to compile shader/light_frag.glsl: %s\n", info);
|
||||
free(vert_code);
|
||||
free(frag_code);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pass->prog = glCreateProgram();
|
||||
glAttachShader(pass->prog, vert);
|
||||
glAttachShader(pass->prog, frag);
|
||||
glLinkProgram(pass->prog);
|
||||
glGetProgramiv(pass->prog, GL_LINK_STATUS, &result);
|
||||
if (result != GL_TRUE) {
|
||||
char info[512];
|
||||
glGetProgramInfoLog(pass->prog, sizeof(info), NULL, info);
|
||||
fprintf(stderr, "Failed to link lighting pass program: %s\n", info);
|
||||
free(vert_code);
|
||||
free(frag_code);
|
||||
return 0;
|
||||
}
|
||||
glDeleteShader(vert);
|
||||
glDeleteShader(frag);
|
||||
free(vert_code);
|
||||
free(frag_code);
|
||||
|
||||
glGenBuffers(1, &pass->lights_sbo);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, pass->lights_sbo);
|
||||
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(Light) * MAX_LIGHTS, NULL, GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int CreateToneMappingPass(ToneMappingPass *pass) {
|
||||
/* Load vertex & fragment shader */
|
||||
char *vert_code = LoadShader("shader/tone_map_vert.glsl");
|
||||
if (!vert_code)
|
||||
return 0;
|
||||
char *frag_code = LoadShader("shader/tone_map_frag.glsl");
|
||||
if (!frag_code) {
|
||||
free(vert_code);
|
||||
return 0;
|
||||
}
|
||||
|
||||
GLint result;
|
||||
GLuint vert = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(vert, 1, &vert_code, NULL);
|
||||
glCompileShader(vert);
|
||||
glGetShaderiv(vert, GL_COMPILE_STATUS, &result);
|
||||
if (result != GL_TRUE) {
|
||||
char info[512];
|
||||
glGetShaderInfoLog(vert, sizeof(info), NULL, info);
|
||||
fprintf(stderr, "Failed to compile shader/tone_map_vert.glsl: %s\n", info);
|
||||
free(vert_code);
|
||||
free(frag_code);
|
||||
return 0;
|
||||
}
|
||||
|
||||
GLuint frag = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(frag, 1, &frag_code, NULL);
|
||||
glCompileShader(frag);
|
||||
glGetShaderiv(frag, GL_COMPILE_STATUS, &result);
|
||||
if (result != GL_TRUE) {
|
||||
char info[512];
|
||||
glGetShaderInfoLog(frag, sizeof(info), NULL, info);
|
||||
fprintf(stderr, "Failed to compile shader/tone_map_frag.glsl: %s\n", info);
|
||||
free(vert_code);
|
||||
free(frag_code);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pass->prog = glCreateProgram();
|
||||
glAttachShader(pass->prog, vert);
|
||||
glAttachShader(pass->prog, frag);
|
||||
glLinkProgram(pass->prog);
|
||||
glGetProgramiv(pass->prog, GL_LINK_STATUS, &result);
|
||||
if (result != GL_TRUE) {
|
||||
char info[512];
|
||||
glGetProgramInfoLog(pass->prog, sizeof(info), NULL, info);
|
||||
fprintf(stderr, "Failed to link lighting pass program: %s\n", info);
|
||||
free(vert_code);
|
||||
free(frag_code);
|
||||
return 0;
|
||||
}
|
||||
glDeleteShader(vert);
|
||||
glDeleteShader(frag);
|
||||
free(vert_code);
|
||||
free(frag_code);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int CreatePixelClassifierPass(PixelClassifierPass *pass) {
|
||||
/* Load vertex & fragment shader */
|
||||
char *code = LoadShader("shader/pixel_classifier.glsl");
|
||||
if (!code)
|
||||
return 0;
|
||||
GLint result;
|
||||
GLuint comp = glCreateShader(GL_COMPUTE_SHADER);
|
||||
glShaderSource(comp, 1, &code, NULL);
|
||||
glCompileShader(comp);
|
||||
glGetShaderiv(comp, GL_COMPILE_STATUS, &result);
|
||||
if (result != GL_TRUE) {
|
||||
char info[512];
|
||||
glGetShaderInfoLog(comp, sizeof(info), NULL, info);
|
||||
fprintf(stderr, "Failed to compile shader/pixel_classifier.glsl: %s\n", info);
|
||||
free(code);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pass->prog = glCreateProgram();
|
||||
glAttachShader(pass->prog, comp);
|
||||
glLinkProgram(pass->prog);
|
||||
glGetProgramiv(pass->prog, GL_LINK_STATUS, &result);
|
||||
if (result != GL_TRUE) {
|
||||
char info[512];
|
||||
glGetProgramInfoLog(pass->prog, sizeof(info), NULL, info);
|
||||
fprintf(stderr, "Failed to link lighting pass program: %s\n", info);
|
||||
free(code);
|
||||
return 0;
|
||||
}
|
||||
glDeleteShader(comp);
|
||||
free(code);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void CreateDifferentiationBuffer(int w, int h, DifferentiationBuffer *diff_buf) {
|
||||
glGenTextures(1, &diff_buf->classification);
|
||||
|
||||
/* position buffer */
|
||||
glBindTexture(GL_TEXTURE_2D, diff_buf->classification);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, w, h, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, NULL);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
static void DestroyGBuffer(GBuffer *gbuf) {
|
||||
glDeleteFramebuffers(1, &gbuf->fbo);
|
||||
glDeleteRenderbuffers(1, &gbuf->depth);
|
||||
glDeleteTextures(1, &gbuf->orm);
|
||||
glDeleteTextures(1, &gbuf->normal);
|
||||
glDeleteTextures(1, &gbuf->albedo);
|
||||
glDeleteTextures(1, &gbuf->position);
|
||||
memset(gbuf, 0, sizeof(*gbuf));
|
||||
}
|
||||
|
||||
static void DestroyGeometryPass(GeometryPass *pass) {
|
||||
glDeleteProgram(pass->prog);
|
||||
memset(pass, 0, sizeof(*pass));
|
||||
}
|
||||
|
||||
static void DestroyHDRBuffer(HDRBuffer *hdr_buf) {
|
||||
glDeleteFramebuffers(1, &hdr_buf->fbo);
|
||||
glDeleteTextures(1, &hdr_buf->color);
|
||||
memset(hdr_buf, 0, sizeof(*hdr_buf));
|
||||
}
|
||||
|
||||
static void DestroyLightingPass(LightingPass *pass) {
|
||||
glDeleteProgram(pass->prog);
|
||||
glDeleteBuffers(1, &pass->lights_sbo);
|
||||
memset(pass, 0, sizeof(*pass));
|
||||
}
|
||||
|
||||
static void DestroyToneMappingPass(ToneMappingPass *pass) {
|
||||
glDeleteProgram(pass->prog);
|
||||
memset(pass, 0, sizeof(*pass));
|
||||
}
|
||||
|
||||
static void DestroyPixelClassificationPass(PixelClassifierPass *pass) {
|
||||
glDeleteProgram(pass->prog);
|
||||
memset(pass, 0, sizeof(*pass));
|
||||
}
|
||||
|
||||
static void DestroyDifferentiationBuffer(DifferentiationBuffer *diff_buf) {
|
||||
glDeleteTextures(1, &diff_buf->classification);
|
||||
memset(diff_buf, 0, sizeof(*diff_buf));
|
||||
}
|
||||
|
||||
/* Create the deferred renderer.
|
||||
* w and h give the size of the gbuffer in pixels. */
|
||||
DeferredRenderer *CreateDeferredRenderer(int w, int h, float near, float far) {
|
||||
DeferredRenderer *renderer = malloc(sizeof(DeferredRenderer));
|
||||
if (!renderer)
|
||||
return NULL;
|
||||
memset(renderer, 0, sizeof(*renderer));
|
||||
|
||||
CreateGBuffer(w, h, &renderer->gbuf);
|
||||
if (!CreateGeometryPass(&renderer->geometry_pass)) {
|
||||
DestroyGBuffer(&renderer->gbuf);
|
||||
free(renderer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CreateHDRBuffer(w, h, &renderer->hdr_buf);
|
||||
if (!CreateLightingPass(&renderer->lighting_pass)) {
|
||||
DestroyHDRBuffer(&renderer->hdr_buf);
|
||||
DestroyGeometryPass(&renderer->geometry_pass);
|
||||
DestroyGBuffer(&renderer->gbuf);
|
||||
free(renderer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!CreateToneMappingPass(&renderer->tone_mapping_pass)) {
|
||||
DestroyLightingPass(&renderer->lighting_pass);
|
||||
DestroyHDRBuffer(&renderer->hdr_buf);
|
||||
DestroyGeometryPass(&renderer->geometry_pass);
|
||||
DestroyGBuffer(&renderer->gbuf);
|
||||
free(renderer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!CreatePixelClassifierPass(&renderer->classifier_pass)) {
|
||||
DestroyToneMappingPass(&renderer->tone_mapping_pass);
|
||||
DestroyLightingPass(&renderer->lighting_pass);
|
||||
DestroyHDRBuffer(&renderer->hdr_buf);
|
||||
DestroyGeometryPass(&renderer->geometry_pass);
|
||||
DestroyGBuffer(&renderer->gbuf);
|
||||
free(renderer);
|
||||
}
|
||||
CreateDifferentiationBuffer(w, h, &renderer->diff_buffer);
|
||||
|
||||
glGenVertexArrays(1, &renderer->full_screen_triangle_vao);
|
||||
glBindVertexArray(renderer->full_screen_triangle_vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
|
||||
renderer->light_count = 0;
|
||||
renderer->light_ssbo_needs_update = 0;
|
||||
renderer->projection = Perspective(DegToRad(45.f), (float)w / (float)h, near, far);
|
||||
|
||||
renderer->width = w;
|
||||
renderer->height = h;
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
/* Free all resources allocated for the deferred renderer. */
|
||||
void DestroyDeferredRenderer(DeferredRenderer *ren) {
|
||||
DestroyDifferentiationBuffer(&ren->diff_buffer);
|
||||
DestroyPixelClassificationPass(&ren->classifier_pass);
|
||||
DestroyToneMappingPass(&ren->tone_mapping_pass);
|
||||
DestroyLightingPass(&ren->lighting_pass);
|
||||
DestroyHDRBuffer(&ren->hdr_buf);
|
||||
DestroyGeometryPass(&ren->geometry_pass);
|
||||
DestroyGBuffer(&ren->gbuf);
|
||||
free(ren);
|
||||
}
|
||||
|
||||
void BeginFrame(DeferredRenderer *renderer, V3 eye_pos, V3 center, V3 up, V3 ambient_color) {
|
||||
renderer->eye_pos = eye_pos;
|
||||
renderer->center = center;
|
||||
renderer->up = up;
|
||||
renderer->ambient_color = ambient_color;
|
||||
|
||||
if (renderer->light_ssbo_needs_update) {
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, renderer->lighting_pass.lights_sbo);
|
||||
glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(Light) * renderer->light_count, renderer->lights);
|
||||
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
|
||||
renderer->light_ssbo_needs_update = 0;
|
||||
}
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, renderer->gbuf.fbo);
|
||||
glClearColor(0.f, 0.f, 0.f, 1.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
}
|
||||
|
||||
void AddLight(DeferredRenderer *renderer, V3 center, V3 color, float radius) {
|
||||
if (renderer->light_count == MAX_LIGHTS)
|
||||
return;
|
||||
Light l;
|
||||
l.position_radius = (V4){ .x = center.x, .y = center.y, .z = center.z, .w = radius };
|
||||
l.color = (V4){ color.x, color.y, color.z, 0.f };
|
||||
renderer->lights[renderer->light_count++] = l;
|
||||
renderer->light_ssbo_needs_update = 1;
|
||||
}
|
||||
|
||||
void RenderModel(DeferredRenderer *renderer, const Model *model, M4 transform) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, renderer->gbuf.fbo);
|
||||
glUseProgram(renderer->geometry_pass.prog);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
/* View & Projection */
|
||||
FrameMatrices frame_matrices;
|
||||
frame_matrices.projection = renderer->projection;
|
||||
frame_matrices.view = LookAt(renderer->eye_pos, renderer->center, renderer->up);
|
||||
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, renderer->geometry_pass.frame_matrices_ubo);
|
||||
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(frame_matrices), &frame_matrices);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, GEOMETRY_PASS_FRAME_MATRICES_BINDING, renderer->geometry_pass.frame_matrices_ubo);
|
||||
|
||||
glUniform1i(GEOMETRY_PASS_ALBEDO_LOCATION, 0);
|
||||
glUniform1i(GEOMETRY_PASS_NORMAL_LOCATION, 1);
|
||||
glUniform1i(GEOMETRY_PASS_ORM_LOCATION, 2);
|
||||
|
||||
for (unsigned int i = 0; i < model->mesh_count; ++i) {
|
||||
glBindVertexArray(model->meshes[i].vao);
|
||||
glUniformMatrix4fv(GEOMETRY_PASS_MODEL_LOCATION, 1, GL_FALSE, &transform.e[0]);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, model->meshes[i].material->albedo->tex);
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, model->meshes[i].material->normal->tex);
|
||||
glActiveTexture(GL_TEXTURE2);
|
||||
glBindTexture(GL_TEXTURE_2D, model->meshes[i].material->orm->tex);
|
||||
glDrawElements(GL_TRIANGLES, model->meshes[i].index_count, GL_UNSIGNED_INT, NULL);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void ResolveGBuffer(DeferredRenderer *renderer) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, renderer->hdr_buf.fbo);
|
||||
glUseProgram(renderer->lighting_pass.prog);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
glClearColor(0.f, 0.f, 0.f, 1.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glUniform1ui(LIGHTING_PASS_LIGHT_COUNT_LOCATION, renderer->light_count);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, LIGHTING_PASS_LIGHTS_BINDING, renderer->lighting_pass.lights_sbo);
|
||||
|
||||
glUniform3f(LIGHTING_PASS_AMBIENT_COLOR_LOCATION, renderer->ambient_color.x, renderer->ambient_color.y, renderer->ambient_color.z);
|
||||
|
||||
glUniform1i(LIGHTING_PASS_POSITION_LOCATION, 0);
|
||||
glUniform1i(LIGHTING_PASS_ALBEDO_LOCATION, 1);
|
||||
glUniform1i(LIGHTING_PASS_NORMAL_LOCATION, 2);
|
||||
glUniform1i(LIGHTING_PASS_ORM_LOCATION, 3);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, renderer->gbuf.position);
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, renderer->gbuf.albedo);
|
||||
glActiveTexture(GL_TEXTURE2);
|
||||
glBindTexture(GL_TEXTURE_2D, renderer->gbuf.normal);
|
||||
glActiveTexture(GL_TEXTURE3);
|
||||
glBindTexture(GL_TEXTURE_2D, renderer->gbuf.orm);
|
||||
|
||||
glBindVertexArray(renderer->full_screen_triangle_vao);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
glBindVertexArray(0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
||||
}
|
||||
|
||||
void ToneMapping(DeferredRenderer *renderer) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glUseProgram(renderer->tone_mapping_pass.prog);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
glClearColor(0.f, 0.f, 0.f, 1.f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glUniform1i(TONE_MAPPING_PASS_COLOR_LOCATION, 0);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, renderer->hdr_buf.color);
|
||||
|
||||
glBindVertexArray(renderer->full_screen_triangle_vao);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
void Differentiate(DeferredRenderer *renderer) {
|
||||
glUseProgram(renderer->classifier_pass.prog);
|
||||
GLuint groups_x = (GLuint)(renderer->width + 15) / 16;
|
||||
GLuint groups_y = (GLuint)(renderer->height + 15) / 16;
|
||||
|
||||
glBindImageTexture(PIXEL_CLASSIFIER_PASS_NORMAL_BINDING, renderer->gbuf.normal, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA16F);
|
||||
glBindImageTexture(PIXEL_CLASSIFIER_PASS_CLASSIFICATION_BINDING, renderer->diff_buffer.classification, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R8UI);
|
||||
|
||||
glDispatchCompute(groups_x, groups_y, 1);
|
||||
}
|
43
src/diffren.h
Normal file
43
src/diffren.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
#ifndef DIFFREN_H
|
||||
#define DIFFREN_H
|
||||
|
||||
#include "diffren_math.h"
|
||||
|
||||
/* Mark as unused to silence compiler warnings about unused parameters */
|
||||
#define UNUSED(x) ((void)sizeof((x)))
|
||||
|
||||
/* Handles rendering of scenes */
|
||||
typedef struct DeferredRenderer DeferredRenderer;
|
||||
|
||||
/* Opaque handle for an imported 3d model */
|
||||
typedef struct Model Model;
|
||||
|
||||
/* Create the deferred renderer.
|
||||
* w and h are the size of the gbuffer in pixels.
|
||||
* near and far define the near and far plane distance. */
|
||||
DeferredRenderer *CreateDeferredRenderer(int w, int h, float near, float far);
|
||||
|
||||
/* Free all resources allocated for the deferred renderer. */
|
||||
void DestroyDeferredRenderer(DeferredRenderer *renderer);
|
||||
|
||||
void BeginFrame(DeferredRenderer *renderer, V3 eye_pos, V3 center, V3 up, V3 ambient_color);
|
||||
|
||||
void AddLight(DeferredRenderer *renderer, V3 center, V3 color, float radius);
|
||||
|
||||
/* Render a model into the scene */
|
||||
void RenderModel(DeferredRenderer *renderer, const Model *model, M4 transform);
|
||||
|
||||
/* Resolve the geometry buffer and lights into a single rendered image */
|
||||
void ResolveGBuffer(DeferredRenderer *renderer);
|
||||
|
||||
void ToneMapping(DeferredRenderer *renderer);
|
||||
|
||||
void Differentiate(DeferredRenderer *renderer);
|
||||
|
||||
/* Import a 3d model from the given file */
|
||||
Model *ImportModel(const char *file);
|
||||
|
||||
/* Destroy an imported model */
|
||||
void DestroyModel(Model *model);
|
||||
|
||||
#endif
|
82
src/diffren_math.h
Normal file
82
src/diffren_math.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
#ifndef DIFFREN_MATH_H
|
||||
#define DIFFREN_MATH_H
|
||||
|
||||
/* Math definitions */
|
||||
|
||||
/* 3d float vector */
|
||||
typedef union {
|
||||
struct {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
};
|
||||
float e[3];
|
||||
} V3;
|
||||
|
||||
/* 2d float vector */
|
||||
typedef union {
|
||||
struct {
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
float e[2];
|
||||
} V2;
|
||||
|
||||
/* 4d float vector */
|
||||
typedef union {
|
||||
struct {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float w;
|
||||
};
|
||||
float e[4];
|
||||
} V4;
|
||||
|
||||
/* 4x4 float matrix */
|
||||
typedef union {
|
||||
V4 columns[4];
|
||||
float e[4 * 4];
|
||||
} M4;
|
||||
|
||||
inline V3 V3Sub(V3 a, V3 b) {
|
||||
V3 s;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
s.e[i] = a.e[i] - b.e[i];
|
||||
return s;
|
||||
}
|
||||
|
||||
inline V3 V3Add(V3 a, V3 b) {
|
||||
V3 s;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
s.e[i] = a.e[i] + b.e[i];
|
||||
return s;
|
||||
}
|
||||
|
||||
inline V3 V3Cross(V3 a, V3 b) {
|
||||
V3 r;
|
||||
r.x = a.y * b.z - a.z * b.y;
|
||||
r.y = a.z * b.x - a.x * b.z;
|
||||
r.z = a.x * b.y - a.y * b.x;
|
||||
return r;
|
||||
}
|
||||
|
||||
inline float DegToRad(float deg) {
|
||||
return deg * (3.1415926f / 180.f);
|
||||
}
|
||||
|
||||
V3 V3Norm(V3 v);
|
||||
|
||||
M4 LookAt(V3 eye, V3 center, V3 up);
|
||||
|
||||
M4 Perspective(float y_fov, float aspect, float n, float f);
|
||||
|
||||
M4 M4Mul(M4 a, M4 b);
|
||||
|
||||
M4 M4Identity(void);
|
||||
|
||||
M4 M4Translate(float x, float y, float z);
|
||||
|
||||
M4 M4Rotate(float x, float y, float z, float angle);
|
||||
|
||||
#endif
|
62
src/diffren_model.h
Normal file
62
src/diffren_model.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
#ifndef DIFFREN_MODEL_H
|
||||
#define DIFFREN_MODEL_H
|
||||
|
||||
/* 3D model structures */
|
||||
|
||||
#include <glad/gl.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "diffren_math.h"
|
||||
|
||||
typedef struct {
|
||||
V3 position;
|
||||
V3 normal;
|
||||
V3 tangent;
|
||||
V2 texcoord;
|
||||
} Vertex;
|
||||
|
||||
typedef struct {
|
||||
int width;
|
||||
int height;
|
||||
int channels;
|
||||
|
||||
char *name;
|
||||
|
||||
/* OpenGL objects */
|
||||
GLuint tex;
|
||||
} Texture;
|
||||
|
||||
typedef struct {
|
||||
Texture *albedo;
|
||||
Texture *normal;
|
||||
Texture *orm;
|
||||
} Material;
|
||||
|
||||
typedef struct {
|
||||
Vertex *vertices;
|
||||
uint32_t *indices;
|
||||
unsigned int vertex_count;
|
||||
unsigned int index_count;
|
||||
|
||||
Material *material;
|
||||
|
||||
/* OpenGL objects */
|
||||
GLuint vbo;
|
||||
GLuint ebo;
|
||||
GLuint vao;
|
||||
} Mesh;
|
||||
|
||||
typedef struct Model {
|
||||
Mesh *meshes;
|
||||
unsigned int mesh_count;
|
||||
|
||||
Material *materials;
|
||||
unsigned int material_count;
|
||||
|
||||
Texture *textures;
|
||||
unsigned int texture_count;
|
||||
|
||||
char *directory;
|
||||
} Model;
|
||||
|
||||
#endif
|
136
src/main.c
Normal file
136
src/main.c
Normal file
|
@ -0,0 +1,136 @@
|
|||
#include <glad/gl.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "diffren.h"
|
||||
|
||||
void APIENTRY glDebugOutput(GLenum source,
|
||||
GLenum type,
|
||||
unsigned int id,
|
||||
GLenum severity,
|
||||
GLsizei length,
|
||||
const char *message,
|
||||
const void *userParam)
|
||||
{
|
||||
UNUSED(length);
|
||||
UNUSED(userParam);
|
||||
|
||||
// ignore non-significant error/warning codes
|
||||
if (id == 131169 || id == 131185 || id == 131218 || id == 131204)
|
||||
return;
|
||||
|
||||
fprintf(stderr, "---------------\n");
|
||||
fprintf(stderr, "Debug message (%u): %s\n", id, message);
|
||||
|
||||
switch (source)
|
||||
{
|
||||
case GL_DEBUG_SOURCE_API: fprintf(stderr, "Source: API"); break;
|
||||
case GL_DEBUG_SOURCE_WINDOW_SYSTEM: fprintf(stderr, "Source: Window System"); break;
|
||||
case GL_DEBUG_SOURCE_SHADER_COMPILER: fprintf(stderr, "Source: Shader Compiler"); break;
|
||||
case GL_DEBUG_SOURCE_THIRD_PARTY: fprintf(stderr, "Source: Third Party"); break;
|
||||
case GL_DEBUG_SOURCE_APPLICATION: fprintf(stderr, "Source: Application"); break;
|
||||
case GL_DEBUG_SOURCE_OTHER: fprintf(stderr, "Source: Other"); break;
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case GL_DEBUG_TYPE_ERROR: fprintf(stderr, "Type: Error"); break;
|
||||
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: fprintf(stderr, "Type: Deprecated Behaviour"); break;
|
||||
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: fprintf(stderr, "Type: Undefined Behaviour"); break;
|
||||
case GL_DEBUG_TYPE_PORTABILITY: fprintf(stderr, "Type: Portability"); break;
|
||||
case GL_DEBUG_TYPE_PERFORMANCE: fprintf(stderr, "Type: Performance"); break;
|
||||
case GL_DEBUG_TYPE_MARKER: fprintf(stderr, "Type: Marker"); break;
|
||||
case GL_DEBUG_TYPE_PUSH_GROUP: fprintf(stderr, "Type: Push Group"); break;
|
||||
case GL_DEBUG_TYPE_POP_GROUP: fprintf(stderr, "Type: Pop Group"); break;
|
||||
case GL_DEBUG_TYPE_OTHER: fprintf(stderr, "Type: Other"); break;
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
switch (severity)
|
||||
{
|
||||
case GL_DEBUG_SEVERITY_HIGH: fprintf(stderr, "Severity: high"); break;
|
||||
case GL_DEBUG_SEVERITY_MEDIUM: fprintf(stderr, "Severity: medium"); break;
|
||||
case GL_DEBUG_SEVERITY_LOW: fprintf(stderr, "Severity: low"); break;
|
||||
case GL_DEBUG_SEVERITY_NOTIFICATION: fprintf(stderr, "Severity: notification"); break;
|
||||
}
|
||||
fprintf(stderr, "\n\n");
|
||||
}
|
||||
|
||||
static void SetupDebugOutput() {
|
||||
int flags;
|
||||
glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
|
||||
if (flags & GL_CONTEXT_FLAG_DEBUG_BIT) {
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
glDebugMessageCallback(glDebugOutput, NULL);
|
||||
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
UNUSED(argc);
|
||||
UNUSED(argv);
|
||||
|
||||
if (!glfwInit())
|
||||
return 1;
|
||||
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
|
||||
#ifndef NDEBUG
|
||||
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE);
|
||||
#endif
|
||||
GLFWwindow *window = glfwCreateWindow(1024, 768, "diffren", NULL, NULL);
|
||||
if (!window) {
|
||||
glfwTerminate();
|
||||
return 1;
|
||||
}
|
||||
glfwMakeContextCurrent(window);
|
||||
if (!gladLoadGL((GLADloadfunc)glfwGetProcAddress)) {
|
||||
glfwDestroyWindow(window);
|
||||
glfwTerminate();
|
||||
return 1;
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
SetupDebugOutput();
|
||||
#endif
|
||||
/* Vsync */
|
||||
glfwSwapInterval(1);
|
||||
|
||||
int fbuf_w, fbuf_h;
|
||||
glfwGetFramebufferSize(window, &fbuf_w, &fbuf_h);
|
||||
DeferredRenderer *renderer = CreateDeferredRenderer(fbuf_w, fbuf_h, 0.1f, 100.f);
|
||||
|
||||
Model *waterbottle = ImportModel("model/WaterBottle.gltf");
|
||||
|
||||
AddLight(renderer, (V3) { -1.f, -1.f, -1.f, }, (V3) { 12.f, 12.f, 4.f }, 3.f);
|
||||
AddLight(renderer, (V3) { 1.f, 1.f, -1.f, }, (V3) { 2.f, 2.f, 20.f }, 3.f);
|
||||
|
||||
float angle = 0.f;
|
||||
|
||||
while (!glfwWindowShouldClose(window)) {
|
||||
glfwPollEvents();
|
||||
|
||||
BeginFrame(renderer, (V3) { -1.f, 0.f, 0.f }, (V3) { 0.f, 0.f, 0.f }, (V3) { 0.f, 1.f, 0.f }, (V3) { 0.3f, 0.3f, 0.3f });
|
||||
|
||||
M4 transform = M4Rotate(1.f, 0.f, 1.f, DegToRad(angle));
|
||||
RenderModel(renderer, waterbottle, transform);
|
||||
|
||||
transform = M4Mul(M4Translate(0.f, 0.f, 0.2f), M4Rotate(0.f, 1.f, 0.f, DegToRad(angle)));
|
||||
RenderModel(renderer, waterbottle, transform);
|
||||
|
||||
angle += 0.5f;
|
||||
|
||||
ResolveGBuffer(renderer);
|
||||
ToneMapping(renderer);
|
||||
Differentiate(renderer);
|
||||
|
||||
glfwSwapBuffers(window);
|
||||
}
|
||||
|
||||
DestroyDeferredRenderer(renderer);
|
||||
glfwDestroyWindow(window);
|
||||
glfwTerminate();
|
||||
return 0;
|
||||
}
|
199
src/math.c
Normal file
199
src/math.c
Normal file
|
@ -0,0 +1,199 @@
|
|||
#include "diffren_math.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
static float V4MulInner(V4 a, V4 b) {
|
||||
float p = 0.f;
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i)
|
||||
p += b.e[i] * a.e[i];
|
||||
return p;
|
||||
}
|
||||
|
||||
static float V3Length(V3 v) {
|
||||
return sqrtf(v.x * v.x + v.y * v.y + v.z * v.z);
|
||||
}
|
||||
|
||||
V3 V3Norm(V3 v) {
|
||||
float len = sqrtf(v.x * v.x + v.y * v.y + v.z * v.z);
|
||||
V3 n = { .x = v.x / len, .y = v.y / len, .z = v.z / len };
|
||||
return n;
|
||||
}
|
||||
|
||||
static V4 Mat4Row(const M4 *m, int i)
|
||||
{
|
||||
V4 r;
|
||||
int k;
|
||||
for (k = 0; k < 4; ++k)
|
||||
r.e[k] = m->columns[k].e[i];
|
||||
return r;
|
||||
}
|
||||
|
||||
static void M4TranslateInPlace(M4 *m, float x, float y, float z)
|
||||
{
|
||||
V4 t = { x, y, z, 0 };
|
||||
V4 r;
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
r = Mat4Row(m, i);
|
||||
m->columns[3].e[i] += V4MulInner(r, t);
|
||||
}
|
||||
}
|
||||
|
||||
M4 M4Add(M4 a, M4 b) {
|
||||
M4 s;
|
||||
for (int i = 0; i < 4 * 4; ++i)
|
||||
s.e[i] = a.e[i] + b.e[i];
|
||||
return s;
|
||||
}
|
||||
|
||||
M4 M4Sub(M4 a, M4 b) {
|
||||
M4 s;
|
||||
for (int i = 0; i < 4 * 4; ++i)
|
||||
s.e[i] = a.e[i] - b.e[i];
|
||||
return s;
|
||||
}
|
||||
|
||||
M4 M4Mul(M4 a, M4 b)
|
||||
{
|
||||
M4 temp;
|
||||
int k, r, c;
|
||||
for (c = 0; c < 4; ++c) {
|
||||
for (r = 0; r < 4; ++r) {
|
||||
temp.columns[c].e[r] = 0.f;
|
||||
for (k = 0; k < 4; ++k)
|
||||
temp.columns[c].e[r] += a.columns[k].e[r] * b.columns[c].e[k];
|
||||
}
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
M4 M4FromV3Outer(V3 a, V3 b)
|
||||
{
|
||||
M4 M;
|
||||
int i, j;
|
||||
for (i = 0; i < 4; ++i) for (j = 0; j < 4; ++j)
|
||||
M.columns[i].e[j] = i < 3 && j < 3 ? a.e[i] * b.e[j] : 0.f;
|
||||
return M;
|
||||
}
|
||||
|
||||
M4 LookAt(V3 eye, V3 center, V3 up) {
|
||||
M4 m;
|
||||
V3 f = V3Sub(center, eye);
|
||||
f = V3Norm(f);
|
||||
|
||||
V3 s = V3Cross(f, up);
|
||||
s = V3Norm(s);
|
||||
|
||||
V3 t = V3Cross(s, f);
|
||||
|
||||
m.columns[0].e[0] = s.x;
|
||||
m.columns[0].e[1] = t.x;
|
||||
m.columns[0].e[2] = -f.x;
|
||||
m.columns[0].e[3] = 0.f;
|
||||
|
||||
m.columns[1].e[0] = s.y;
|
||||
m.columns[1].e[1] = t.y;
|
||||
m.columns[1].e[2] = -f.y;
|
||||
m.columns[1].e[3] = 0.f;
|
||||
|
||||
m.columns[2].e[0] = s.z;
|
||||
m.columns[2].e[1] = t.z;
|
||||
m.columns[2].e[2] = -f.z;
|
||||
m.columns[2].e[3] = 0.f;
|
||||
|
||||
m.columns[3].e[0] = 0.f;
|
||||
m.columns[3].e[1] = 0.f;
|
||||
m.columns[3].e[2] = 0.f;
|
||||
m.columns[3].e[3] = 1.f;
|
||||
|
||||
M4TranslateInPlace(&m, -eye.x, -eye.y, -eye.z);
|
||||
return m;
|
||||
}
|
||||
|
||||
M4 Perspective(float y_fov, float aspect, float n, float f)
|
||||
{
|
||||
M4 m;
|
||||
float a = 1.f / tanf(y_fov / 2.f);
|
||||
|
||||
m.columns[0].e[0] = a / aspect;
|
||||
m.columns[0].e[1] = 0.f;
|
||||
m.columns[0].e[2] = 0.f;
|
||||
m.columns[0].e[3] = 0.f;
|
||||
|
||||
m.columns[1].e[0] = 0.f;
|
||||
m.columns[1].e[1] = a;
|
||||
m.columns[1].e[2] = 0.f;
|
||||
m.columns[1].e[3] = 0.f;
|
||||
|
||||
m.columns[2].e[0] = 0.f;
|
||||
m.columns[2].e[1] = 0.f;
|
||||
m.columns[2].e[2] = -((f + n) / (f - n));
|
||||
m.columns[2].e[3] = -1.f;
|
||||
|
||||
m.columns[3].e[0] = 0.f;
|
||||
m.columns[3].e[1] = 0.f;
|
||||
m.columns[3].e[2] = -((2.f * f * n) / (f - n));
|
||||
m.columns[3].e[3] = 0.f;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
M4 M4MulScalar(M4 m, float s) {
|
||||
M4 t;
|
||||
for (int i = 0; i < 4 * 4; ++i)
|
||||
t.e[i] = m.e[i] * s;
|
||||
return t;
|
||||
}
|
||||
|
||||
M4 M4Identity(void) {
|
||||
M4 m;
|
||||
memset(&m, 0, sizeof(m));
|
||||
m.columns[0].e[0] = 1.f;
|
||||
m.columns[1].e[1] = 1.f;
|
||||
m.columns[2].e[2] = 1.f;
|
||||
m.columns[3].e[3] = 1.f;
|
||||
return m;
|
||||
}
|
||||
|
||||
M4 M4Translate(float x, float y, float z) {
|
||||
M4 m = M4Identity();
|
||||
m.columns[3].x = x;
|
||||
m.columns[3].y = y;
|
||||
m.columns[3].z = z;
|
||||
return m;
|
||||
}
|
||||
|
||||
M4 M4Rotate(float x, float y, float z, float angle) {
|
||||
float s = sinf(angle);
|
||||
float c = cosf(angle);
|
||||
V3 u = { x, y, z };
|
||||
|
||||
if (V3Length(u) > 1e-4) {
|
||||
u = V3Norm(u);
|
||||
M4 T = M4FromV3Outer(u, u);
|
||||
|
||||
M4 S = {
|
||||
.columns[0] = { 0, u.z, -u.y, 0},
|
||||
.columns[1] = {-u.z, 0, u.x, 0},
|
||||
.columns[2] = { u.y, -u.x, 0, 0},
|
||||
.columns[3] = { 0, 0, 0, 0}
|
||||
};
|
||||
S = M4MulScalar(S, s);
|
||||
|
||||
M4 C = M4Identity();
|
||||
C = M4Sub(C, T);
|
||||
|
||||
C = M4MulScalar(C, c);
|
||||
|
||||
T = M4Add(T, C);
|
||||
T = M4Add(T, S);
|
||||
|
||||
T.columns[3].e[3] = 1.f;
|
||||
return T;
|
||||
}
|
||||
else {
|
||||
return M4Identity();
|
||||
}
|
||||
}
|
284
src/model_loader.c
Normal file
284
src/model_loader.c
Normal file
|
@ -0,0 +1,284 @@
|
|||
#include <assimp/cimport.h>
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/postprocess.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "diffren.h"
|
||||
#include "diffren_model.h"
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
|
||||
/* TODO(Kevin): Handle the node structure.
|
||||
* Handle multiple meshes using the same texture.
|
||||
* Turn allocations into flat preallocated buffers
|
||||
*/
|
||||
|
||||
static int ProcessTexture(struct aiMaterial *mat, enum aiTextureType type, Model *model) {
|
||||
if (aiGetMaterialTextureCount(mat, type) == 0)
|
||||
return 0;
|
||||
|
||||
struct aiString path;
|
||||
if (aiGetMaterialTexture(mat, type, 0, &path, NULL, NULL, NULL, NULL, NULL, NULL) != aiReturn_SUCCESS)
|
||||
return 0;
|
||||
|
||||
Texture tex;
|
||||
tex.name = malloc(path.length + 1);
|
||||
if (!tex.name)
|
||||
return 0;
|
||||
memcpy(tex.name, path.data, path.length + 1);
|
||||
|
||||
char full_path[260];
|
||||
snprintf(full_path, 259, "%s%s", model->directory, path.data);
|
||||
full_path[259] = '\0';
|
||||
|
||||
stbi_uc *pixels = stbi_load(full_path, &tex.width, &tex.height, &tex.channels, 0);
|
||||
if (!pixels) {
|
||||
free(tex.name);
|
||||
return 0;
|
||||
}
|
||||
glGenTextures(1, &tex.tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex.tex);
|
||||
GLenum internal_format, format;
|
||||
if (tex.channels == 3) {
|
||||
internal_format = GL_RGB8;
|
||||
format = GL_RGB;
|
||||
}
|
||||
else if (tex.channels == 4) {
|
||||
internal_format = GL_RGBA8;
|
||||
format = GL_RGBA;
|
||||
}
|
||||
else if (tex.channels == 2) {
|
||||
internal_format = GL_RG8;
|
||||
format = GL_RG;
|
||||
}
|
||||
else if (tex.channels == 1) {
|
||||
internal_format = GL_R8;
|
||||
format = GL_RED;
|
||||
}
|
||||
else {
|
||||
free(tex.name);
|
||||
stbi_image_free(pixels);
|
||||
return 0;
|
||||
}
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, tex.width, tex.height, 0, format, GL_UNSIGNED_BYTE, pixels);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
|
||||
stbi_image_free(pixels);
|
||||
|
||||
model->textures[model->texture_count++] = tex;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ProcessMaterial(struct aiMaterial *in_material, Model *model) {
|
||||
Material mat;
|
||||
if (ProcessTexture(in_material, aiTextureType_BASE_COLOR, model))
|
||||
mat.albedo = &model->textures[model->texture_count - 1];
|
||||
else
|
||||
mat.albedo = NULL;
|
||||
if (ProcessTexture(in_material, aiTextureType_NORMALS, model))
|
||||
mat.normal = &model->textures[model->texture_count - 1];
|
||||
else
|
||||
mat.normal = NULL;
|
||||
if (ProcessTexture(in_material, aiTextureType_DIFFUSE_ROUGHNESS, model))
|
||||
mat.orm = &model->textures[model->texture_count - 1];
|
||||
else
|
||||
mat.orm = NULL;
|
||||
model->materials[model->material_count++] = mat;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ProcessMesh(struct aiMesh *in_mesh, const struct aiScene *scene, Model *model) {
|
||||
Mesh m;
|
||||
m.vertices = malloc(in_mesh->mNumVertices * sizeof(Vertex));
|
||||
if (!m.vertices)
|
||||
return 0;
|
||||
m.indices = malloc(in_mesh->mNumFaces * 3 * sizeof(uint32_t));
|
||||
if (!m.indices) {
|
||||
free(m.vertices);
|
||||
return 0;
|
||||
}
|
||||
m.vertex_count = in_mesh->mNumVertices;
|
||||
m.index_count = 0;
|
||||
|
||||
for (unsigned int i = 0; i < in_mesh->mNumVertices; ++i) {
|
||||
Vertex v;
|
||||
v.position.x = in_mesh->mVertices[i].x;
|
||||
v.position.y = in_mesh->mVertices[i].y;
|
||||
v.position.z = in_mesh->mVertices[i].z;
|
||||
|
||||
v.normal.x = in_mesh->mNormals[i].x;
|
||||
v.normal.y = in_mesh->mNormals[i].y;
|
||||
v.normal.z = in_mesh->mNormals[i].z;
|
||||
|
||||
v.tangent.x = in_mesh->mTangents[i].x;
|
||||
v.tangent.y = in_mesh->mTangents[i].y;
|
||||
v.tangent.z = in_mesh->mTangents[i].z;
|
||||
|
||||
if (in_mesh->mTextureCoords[0]) {
|
||||
v.texcoord.x = in_mesh->mTextureCoords[0][i].x;
|
||||
v.texcoord.y = in_mesh->mTextureCoords[0][i].y;
|
||||
}
|
||||
else {
|
||||
v.texcoord.x = 0.f;
|
||||
v.texcoord.y = 0.f;
|
||||
}
|
||||
|
||||
m.vertices[i] = v;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < in_mesh->mNumFaces; ++i) {
|
||||
struct aiFace face = in_mesh->mFaces[i];
|
||||
for (unsigned int j = 0; j < face.mNumIndices; ++j) {
|
||||
m.indices[m.index_count++] = face.mIndices[j];
|
||||
}
|
||||
}
|
||||
|
||||
if (in_mesh->mMaterialIndex >= 0) {
|
||||
if (!ProcessMaterial(scene->mMaterials[in_mesh->mMaterialIndex], model)) {
|
||||
free(m.vertices);
|
||||
free(m.indices);
|
||||
return 0;
|
||||
}
|
||||
m.material = &model->materials[model->material_count - 1];
|
||||
}
|
||||
else {
|
||||
m.material = NULL;
|
||||
}
|
||||
|
||||
glGenBuffers(1, &m.vbo);
|
||||
glGenBuffers(1, &m.ebo);
|
||||
glGenVertexArrays(1, &m.vao);
|
||||
|
||||
glBindVertexArray(m.vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, m.vbo);
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * m.vertex_count, m.vertices, GL_STATIC_DRAW);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m.ebo);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32_t) * m.index_count, m.indices, GL_STATIC_DRAW);
|
||||
|
||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));
|
||||
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal));
|
||||
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, tangent));
|
||||
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, texcoord));
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glEnableVertexAttribArray(2);
|
||||
glEnableVertexAttribArray(3);
|
||||
|
||||
glBindVertexArray(0);
|
||||
|
||||
model->meshes[model->mesh_count++] = m;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ProcessNode(struct aiNode *node, const struct aiScene *scene, Model *model) {
|
||||
for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
|
||||
struct aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
|
||||
if (!ProcessMesh(mesh, scene, model))
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < node->mNumChildren; ++i) {
|
||||
if (!ProcessNode(node->mChildren[i], scene, model))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Import a 3d model from the given file */
|
||||
Model *ImportModel(const char *file) {
|
||||
Model *model = malloc(sizeof(*model));
|
||||
if (!model) {
|
||||
fprintf(stderr, "Could not import %s: Could not allocate storage.\n", file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Determine directory */
|
||||
model->directory = malloc(strlen(file) + 1);
|
||||
if (!model->directory) {
|
||||
fprintf(stderr, "Could not import %s: Could not allocate directory path storage.\n", file);
|
||||
free(model);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(model->directory, file, strlen(file) + 1);
|
||||
for (int i = (int)strlen(file); i >= 0; --i) {
|
||||
if (model->directory[i] == '/' || model->directory[i] == '\\') {
|
||||
model->directory[i + 1] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const struct aiScene *scene = aiImportFile(file,
|
||||
aiProcess_CalcTangentSpace |
|
||||
aiProcess_Triangulate |
|
||||
aiProcess_JoinIdenticalVertices |
|
||||
aiProcess_SortByPType |
|
||||
aiProcess_OptimizeMeshes |
|
||||
aiProcess_FlipUVs |
|
||||
aiProcess_GenNormals);
|
||||
if (!scene || (scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) != 0 || !scene->mRootNode) {
|
||||
fprintf(stderr, "Could not import %s: %s", file, aiGetErrorString());
|
||||
free(model);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
model->meshes = malloc(sizeof(Mesh) * scene->mNumMeshes);
|
||||
if (!model->meshes) {
|
||||
fprintf(stderr, "Could not import %s: Could not allocate mesh storage.\n", file);
|
||||
free(model);
|
||||
aiReleaseImport(scene);
|
||||
return NULL;
|
||||
}
|
||||
model->mesh_count = 0;
|
||||
|
||||
model->materials = malloc(sizeof(Material) * scene->mNumMaterials);
|
||||
if (!model->materials) {
|
||||
fprintf(stderr, "Could not import %s: Could not allocate material storage.\n", file);
|
||||
free(model->meshes);
|
||||
free(model);
|
||||
aiReleaseImport(scene);
|
||||
return NULL;
|
||||
}
|
||||
model->material_count = 0;
|
||||
|
||||
model->textures = malloc(sizeof(Texture) * scene->mNumMaterials * 3);
|
||||
if (!model->textures) {
|
||||
fprintf(stderr, "Could not import %s: Could not allocate texture storage.\n", file);
|
||||
free(model->textures);
|
||||
free(model->meshes);
|
||||
free(model);
|
||||
aiReleaseImport(scene);
|
||||
return NULL;
|
||||
}
|
||||
model->texture_count = 0;
|
||||
|
||||
if (!ProcessNode(scene->mRootNode, scene, model)) {
|
||||
DestroyModel(model);
|
||||
model = NULL;
|
||||
}
|
||||
|
||||
aiReleaseImport(scene);
|
||||
return model;
|
||||
}
|
||||
|
||||
/* Destroy an imported model */
|
||||
void DestroyModel(Model *model) {
|
||||
free(model->directory);
|
||||
for (unsigned int i = 0; i < model->texture_count; ++i) {
|
||||
free(model->textures[i].name);
|
||||
glDeleteTextures(1, &model->textures[i].tex);
|
||||
}
|
||||
free(model->textures);
|
||||
free(model->materials);
|
||||
for (unsigned int i = 0; i < model->mesh_count; ++i) {
|
||||
free(model->meshes[i].vertices);
|
||||
free(model->meshes[i].indices);
|
||||
glDeleteBuffers(1, &model->meshes[i].vbo);
|
||||
glDeleteBuffers(1, &model->meshes[i].ebo);
|
||||
glDeleteVertexArrays(1, &model->meshes[i].vbo);
|
||||
}
|
||||
free(model->meshes);
|
||||
free(model);
|
||||
}
|
7985
src/stb_image.h
Normal file
7985
src/stb_image.h
Normal file
File diff suppressed because it is too large
Load Diff
3
subprojects/.gitignore
vendored
Normal file
3
subprojects/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
assimp-5.3.1/*
|
||||
glfw-*/*
|
||||
packagecache/*
|
13
subprojects/glfw.wrap
Normal file
13
subprojects/glfw.wrap
Normal file
|
@ -0,0 +1,13 @@
|
|||
[wrap-file]
|
||||
directory = glfw-3.3.9
|
||||
source_url = https://github.com/glfw/glfw/archive/refs/tags/3.3.9.tar.gz
|
||||
source_filename = glfw-3.3.9.tar.gz
|
||||
source_hash = a7e7faef424fcb5f83d8faecf9d697a338da7f7a906fc1afbc0e1879ef31bd53
|
||||
patch_filename = glfw_3.3.9-1_patch.zip
|
||||
patch_url = https://wrapdb.mesonbuild.com/v2/glfw_3.3.9-1/get_patch
|
||||
patch_hash = b4261ed4de479ea0496617feace62eec5b73f62bf3c88dd1537afcf5eebd5cab
|
||||
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/glfw_3.3.9-1/glfw-3.3.9.tar.gz
|
||||
wrapdb_version = 3.3.9-1
|
||||
|
||||
[provide]
|
||||
glfw3 = glfw_dep
|
Loading…
Reference in New Issue
Block a user