normal mapping, basic pbr

This commit is contained in:
BENEDEK László 2024-10-18 16:12:01 +02:00
parent 92b9e651ac
commit 0497ff9ac7
9 changed files with 402 additions and 55 deletions

View File

@ -9,6 +9,7 @@ uniform sampler2D s_metalic;
in vec3 pos; in vec3 pos;
in vec3 norm; in vec3 norm;
in vec2 tex; in vec2 tex;
in mat3 TBN;
layout (location = 0) out vec3 albedo; layout (location = 0) out vec3 albedo;
layout (location = 1) out vec3 normal; layout (location = 1) out vec3 normal;
@ -16,10 +17,10 @@ layout (location = 2) out vec3 material;
layout (location = 3) out vec3 position; layout (location = 3) out vec3 position;
void main() { void main() {
position = pos;
albedo = texture(s_albedo, tex).rgb; albedo = texture(s_albedo, tex).rgb;
normal = normalize(texture(s_normal, tex).rgb * 2.0 - 1.0); normal = normalize(TBN * (texture(s_normal, tex).rgb * 2.0 - 1.0));
material.x = texture(s_specular, tex).x; material.x = texture(s_specular, tex).x;
material.y = texture(s_roughness, tex).y; material.y = texture(s_roughness, tex).y;
material.z = texture(s_metalic, tex).z; material.z = texture(s_metalic, tex).z;
position = pos;
} }

View File

@ -3,6 +3,7 @@
layout (location = 0) in vec3 _pos; layout (location = 0) in vec3 _pos;
layout (location = 1) in vec2 _tex; layout (location = 1) in vec2 _tex;
layout (location = 2) in vec3 _norm; layout (location = 2) in vec3 _norm;
layout (location = 3) in vec3 _tan;
uniform mat4 projection; uniform mat4 projection;
uniform mat4 view; uniform mat4 view;
@ -11,8 +12,14 @@ uniform mat4 model;
out vec3 pos; out vec3 pos;
out vec3 norm; out vec3 norm;
out vec2 tex; out vec2 tex;
out mat3 TBN;
void main() { void main() {
vec3 T = normalize(vec3(model * vec4(_tan, 0)));
vec3 N = normalize(vec3(model * vec4(_norm, 0)));
vec3 B = cross(N, T);
TBN = mat3(T, B, N);
gl_Position = projection * view * model * vec4(_pos, 1.0); gl_Position = projection * view * model * vec4(_pos, 1.0);
pos = vec3(model * vec4(_pos, 1.0)); pos = vec3(model * vec4(_pos, 1.0));
norm = mat3(transpose(inverse(model))) * _norm; norm = mat3(transpose(inverse(model))) * _norm;

151
assets/shader/pbr/pbr.fs Normal file
View File

@ -0,0 +1,151 @@
#version 460 core
// gbuffer textures
uniform sampler2D s_albedo;
uniform sampler2D s_depth;
uniform sampler2D s_normal;
uniform sampler2D s_material;
uniform sampler2D s_position;
// lights
#define MAX_LIGHT_COUNT 100
#define POINT 0
#define SPOT 1
#define DIRECTIONAL 2
#define AMBIENT 3
struct light_t {
vec3 color;
vec3 position;
vec3 direction;
float intensity;
int type;
};
uniform light_t lights[MAX_LIGHT_COUNT];
uniform int light_count;
uniform vec3 view_pos;
// from vertex shader
in vec3 pos;
in vec2 tex;
out vec4 FragColor;
vec3 albedo;
float depth;
vec3 normal;
vec3 material; // specular, roughness, metalic
vec3 position;
// float specular;
float roughness;
float metalic;
const float PI = 3.14159265359;
float DistributionGGX(vec3 N, vec3 H, float roughness)
{
float a = roughness*roughness;
float a2 = a*a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH*NdotH;
float nom = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return nom / denom;
}
float GeometrySchlickGGX(float NdotV, float roughness)
{
float r = (roughness + 1.0);
float k = (r*r) / 8.0;
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
{
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
vec3 fresnelSchlick(float cosTheta, vec3 F0)
{
return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);
}
void main() {
albedo = pow(texture(s_albedo, tex).rgb, vec3(2.2));
depth = texture(s_depth, tex).x;
normal = normalize(texture(s_normal, tex).rgb);
material = texture(s_material, tex).xyz;
{
// specular = material.x;
roughness = 1 - material.y;
metalic = material.z;
// metalic = 0;
}
position = texture(s_position, tex).xyz;
vec3 N = normal;
vec3 V = normalize(view_pos - position);
vec3 F0 = vec3(0.04);
F0 = mix(F0, albedo, metalic);
vec3 Lo = vec3(0.0);
for(int i = 0; i < light_count; i++)
{
// calculate per-light radiance
vec3 L = normalize(lights[i].position - position);
vec3 H = normalize(V + L);
float distance = length(lights[i].position - position);
float attenuation = 1.0 / (distance * distance);
vec3 radiance = lights[i].color * lights[i].intensity * attenuation;
// Cook-Torrance BRDF
float NDF = DistributionGGX(N, H, roughness);
float G = GeometrySmith(N, V, L, roughness);
vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0);
vec3 numerator = NDF * G * F;
float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.0001; // + 0.0001 to prevent divide by zero
vec3 specular = numerator / denominator;
// kS is equal to Fresnel
vec3 kS = F;
// for energy conservation, the diffuse and specular light can't
// be above 1.0 (unless the surface emits light); to preserve this
// relationship the diffuse component (kD) should equal 1.0 - kS.
vec3 kD = vec3(1.0) - kS;
// multiply kD by the inverse metalness such that only non-metals
// have diffuse lighting, or a linear blend if partly metal (pure metals
// have no diffuse light).
kD *= 1.0 - metalic;
// scale light by NdotL
float NdotL = max(dot(N, L), 0.0);
// add to outgoing radiance Lo
Lo += (kD * albedo / PI + specular) * radiance * NdotL; // note that we already multiplied the BRDF by the Fresnel (kS) so we won't multiply by kS again
}
vec3 ambient = vec3(0.03) * albedo * 1.0;
vec3 color = ambient + Lo;
color = color / (color + vec3(1.0));
color = pow(color, vec3(1.0/2.2));
FragColor = vec4(color, 1.0);
}

13
assets/shader/pbr/pbr.vs Normal file
View File

@ -0,0 +1,13 @@
#version 460 core
layout (location = 0) in vec3 _pos;
layout (location = 1) in vec2 _tex;
out vec3 pos;
out vec2 tex;
void main() {
gl_Position = vec4(_pos.xy, 0, 1);
pos = _pos;
tex = _tex;
}

View File

@ -0,0 +1,63 @@
#version 460 core
// gbuffer textures
uniform sampler2D s_albedo;
uniform sampler2D s_depth;
uniform sampler2D s_normal;
uniform sampler2D s_material;
uniform sampler2D s_position;
// lights
#define MAX_LIGHT_COUNT 100
#define POINT 0
#define SPOT 1
#define DIRECTIONAL 2
#define AMBIENT 3
struct light_t {
vec3 color;
vec3 position;
vec3 direction;
float intensity;
int type;
};
uniform light_t lights[MAX_LIGHT_COUNT];
uniform int light_count;
uniform vec3 view_pos;
// from vertex shader
in vec3 pos;
in vec2 tex;
out vec4 FragColor;
vec3 albedo;
float depth;
vec3 normal;
vec3 material; // specular, roughness, metalic
vec3 position;
float specular;
float roughness;
float metalic;
void main() {
// albedo, normal
// material, position
albedo = texture(s_albedo, tex*2).rgb;
depth = texture(s_depth, tex*2).x;
normal = normalize(texture(s_normal, tex*2).rgb);
material = texture(s_material, tex*2).xyz;
{
specular = material.x;
roughness = material.y;
metalic = material.z;
}
position = texture(s_position, tex*2).xyz;
FragColor = vec4(
(pos.x < 0 ? (pos.y < 0 ? material : albedo) : (pos.y < 0 ? position : normal)),
1);
}

View File

@ -38,35 +38,46 @@ float depth;
vec3 normal; vec3 normal;
vec3 material; // specular, roughness, metalic vec3 material; // specular, roughness, metalic
vec3 position; vec3 position;
vec2 texture_coords;
float specular; float specular;
float roughness; float roughness;
float metalic; float metalic;
vec3 calc_light(light_t light) { vec3 calc_light(light_t light) {
// diffuse
vec3 light_dir = normalize(light.position - position); vec3 light_dir = normalize(light.position - position);
float diff = max(dot(normal, light_dir), 0);
vec3 diffuse_light = diff * light.color * light.intensity;
// specular
float specular_strength = 0.5;
vec3 view_dir = normalize(view_pos - position); vec3 view_dir = normalize(view_pos - position);
vec3 reflect_dir = reflect(-light_dir, normal); vec3 reflect_dir = reflect(-light_dir, normal);
float diffuse = max(dot(normal, light_dir), 0.0); float spec = pow(max(dot(view_dir, reflect_dir), 0), 32);
float specular = pow(max(dot(view_dir, reflect_dir), 0.0), 1 - roughness) * specular; vec3 specular_light = specular_strength * spec * light.color * light.intensity * specular;
return (diffuse + specular) * light.intensity * light.color / max(pow(length(light.position - position), 2), 1); // attenuation
float dist = length(light.position - position);
float attenuation = 1 / max(pow(dist, 2), 1);
return (diffuse_light + specular_light) * attenuation;
} }
void main() { void main() {
albedo = texture(s_albedo, tex).rgb; albedo = pow(texture(s_albedo, tex).rgb, vec3(2.2));
depth = texture(s_depth, tex).x; depth = texture(s_depth, tex).x;
normal = texture(s_normal, tex).rgb; normal = normalize(texture(s_normal, tex).rgb);
material = texture(s_material, tex).xyz; material = texture(s_material, tex).xyz;
{ {
specular = material.x; specular = material.x;
roughness = material.y; roughness = 1 - material.y;
metalic = material.z; metalic = material.z;
} }
position = texture(s_position, tex).xyz; position = texture(s_position, tex).xyz;
vec3 light_result; vec3 light_result = vec3(0.1);
for (int i = 0; i < light_count; i++) { for (int i = 0; i < light_count; i++) {
light_result += calc_light(lights[i]); light_result += calc_light(lights[i]);
} }

26
main.go
View File

@ -73,11 +73,13 @@ func main() {
} }
defer deferredShader.Delete() defer deferredShader.Delete()
vertexSource, err = os.ReadFile("assets/shader/screen/screen.vs") vertexSource, err = os.ReadFile("assets/shader/pbr/pbr.vs")
if err != nil { if err != nil {
panic(err) panic(err)
} }
fragmentSource, err = os.ReadFile("assets/shader/screen/screen.fs") // fragmentSource, err = os.ReadFile("assets/shader/screen/screen-debug.fs")
// fragmentSource, err = os.ReadFile("assets/shader/screen/screen.fs")
fragmentSource, err = os.ReadFile("assets/shader/pbr/pbr.fs")
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -144,10 +146,23 @@ func main() {
} }
defer metalic.Delete() defer metalic.Delete()
white, err := texture.Load("assets/textures/white.png")
if err != nil {
panic(err)
}
defer white.Delete()
black, err := texture.Load("assets/textures/black.png")
if err != nil {
panic(err)
}
defer black.Delete()
// lights // lights
lights := []light.Light{ lights := []light.Light{
{Color: [3]float32{0, 1, 0}, Position: mgl32.Vec3{3, 3, 3}, Intensity: 15, Type: light.POINT}, {Color: [3]float32{1, 1, 1}, Position: mgl32.Vec3{0, 10, 0}, Intensity: 40, Type: light.POINT},
{Color: [3]float32{1, 0, 1}, Position: mgl32.Vec3{3, 3, -3}, Intensity: 15, Type: light.POINT}, {Color: [3]float32{1, 0, 0}, Position: mgl32.Vec3{5, 5, 5}, Intensity: 30, Type: light.POINT},
{Color: [3]float32{0, 0, 1}, Position: mgl32.Vec3{-5, 5, -5}, Intensity: 30, Type: light.POINT},
} }
// gbuffer // gbuffer
@ -161,14 +176,13 @@ func main() {
// transformations // transformations
projection := mgl32.Perspective(mgl32.DegToRad(FOV), float32(WIDTH)/HEIGHT, 0.1, 1000) projection := mgl32.Perspective(mgl32.DegToRad(FOV), float32(WIDTH)/HEIGHT, 0.1, 1000)
model := mgl32.Ident4() model := mgl32.Ident4()
controls := fpscontrols.Get(10, 10, mgl32.Vec3{0, 0, -10}, mgl32.Vec2{0, 0}, window) controls := fpscontrols.Get(15, 10, mgl32.Vec3{0, 0, -10}, mgl32.Vec2{0, 0}, window)
for !window.ShouldClose() { for !window.ShouldClose() {
glfw.PollEvents() glfw.PollEvents()
controls.Update() controls.Update()
// first pass // first pass
// bind textures
gl.Enable(gl.DEPTH_TEST) gl.Enable(gl.DEPTH_TEST)
gbuff.Bind() gbuff.Bind()

View File

@ -12,7 +12,7 @@ type GBuffer struct {
colorAttachments [4]uint32 // albedo, normal, material, position colorAttachments [4]uint32 // albedo, normal, material, position
} }
var attachments = []uint32{ var attachments = [...]uint32{
gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT0,
gl.COLOR_ATTACHMENT1, gl.COLOR_ATTACHMENT1,
gl.COLOR_ATTACHMENT2, gl.COLOR_ATTACHMENT2,
@ -25,18 +25,23 @@ func (b *GBuffer) Bind() {
} }
func (b *GBuffer) BindTextures() { func (b *GBuffer) BindTextures() {
// albedo
gl.ActiveTexture(gl.TEXTURE0) gl.ActiveTexture(gl.TEXTURE0)
gl.BindTexture(gl.TEXTURE_2D, b.colorAttachments[0]) gl.BindTexture(gl.TEXTURE_2D, b.colorAttachments[0])
// depth
gl.ActiveTexture(gl.TEXTURE1) gl.ActiveTexture(gl.TEXTURE1)
gl.BindTexture(gl.TEXTURE_2D, b.depth) gl.BindTexture(gl.TEXTURE_2D, b.depth)
// normal
gl.ActiveTexture(gl.TEXTURE2) gl.ActiveTexture(gl.TEXTURE2)
gl.BindTexture(gl.TEXTURE_2D, b.colorAttachments[1]) gl.BindTexture(gl.TEXTURE_2D, b.colorAttachments[1])
// material
gl.ActiveTexture(gl.TEXTURE3) gl.ActiveTexture(gl.TEXTURE3)
gl.BindTexture(gl.TEXTURE_2D, b.colorAttachments[2]) gl.BindTexture(gl.TEXTURE_2D, b.colorAttachments[2])
// position
gl.ActiveTexture(gl.TEXTURE4) gl.ActiveTexture(gl.TEXTURE4)
gl.BindTexture(gl.TEXTURE_2D, b.colorAttachments[3]) gl.BindTexture(gl.TEXTURE_2D, b.colorAttachments[3])
} }
@ -54,14 +59,17 @@ func New(width int, height int) (buffer GBuffer, _ error) {
gl.GenFramebuffers(1, &buffer.framebuffer) gl.GenFramebuffers(1, &buffer.framebuffer)
gl.BindFramebuffer(gl.FRAMEBUFFER, buffer.framebuffer) gl.BindFramebuffer(gl.FRAMEBUFFER, buffer.framebuffer)
var i uint32 = 0
// albedo // albedo
gl.GenTextures(1, &buffer.colorAttachments[0]) gl.GenTextures(1, &buffer.colorAttachments[i])
gl.BindTexture(gl.TEXTURE_2D, buffer.colorAttachments[0]) gl.BindTexture(gl.TEXTURE_2D, buffer.colorAttachments[i])
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGB, int32(width), int32(height), 0, gl.RGB, gl.UNSIGNED_BYTE, nil) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGB, int32(width), int32(height), 0, gl.RGB, gl.FLOAT, nil)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
gl.BindTexture(gl.TEXTURE_2D, 0) gl.BindTexture(gl.TEXTURE_2D, 0)
gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, buffer.colorAttachments[0], 0) gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0+i, gl.TEXTURE_2D, buffer.colorAttachments[i], 0)
i++
// depth // depth
gl.GenTextures(1, &buffer.depth) gl.GenTextures(1, &buffer.depth)
@ -73,31 +81,34 @@ func New(width int, height int) (buffer GBuffer, _ error) {
gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, buffer.depth, 0) gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, buffer.depth, 0)
// normal // normal
gl.GenTextures(1, &buffer.colorAttachments[1]) gl.GenTextures(1, &buffer.colorAttachments[i])
gl.BindTexture(gl.TEXTURE_2D, buffer.colorAttachments[1]) gl.BindTexture(gl.TEXTURE_2D, buffer.colorAttachments[i])
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGB, int32(width), int32(height), 0, gl.RGB, gl.UNSIGNED_BYTE, nil)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
gl.BindTexture(gl.TEXTURE_2D, 0)
gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.TEXTURE_2D, buffer.colorAttachments[1], 0)
// material
gl.GenTextures(1, &buffer.colorAttachments[2])
gl.BindTexture(gl.TEXTURE_2D, buffer.colorAttachments[2])
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGB, int32(width), int32(height), 0, gl.RGB, gl.UNSIGNED_BYTE, nil)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
gl.BindTexture(gl.TEXTURE_2D, 0)
gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT2, gl.TEXTURE_2D, buffer.colorAttachments[2], 0)
// position
gl.GenTextures(1, &buffer.colorAttachments[3])
gl.BindTexture(gl.TEXTURE_2D, buffer.colorAttachments[3])
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGB, int32(width), int32(height), 0, gl.RGB, gl.FLOAT, nil) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGB, int32(width), int32(height), 0, gl.RGB, gl.FLOAT, nil)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
gl.BindTexture(gl.TEXTURE_2D, 0) gl.BindTexture(gl.TEXTURE_2D, 0)
gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT3, gl.TEXTURE_2D, buffer.colorAttachments[3], 0) gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0+i, gl.TEXTURE_2D, buffer.colorAttachments[i], 0)
i++
// material
gl.GenTextures(1, &buffer.colorAttachments[i])
gl.BindTexture(gl.TEXTURE_2D, buffer.colorAttachments[i])
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGB, int32(width), int32(height), 0, gl.RGB, gl.FLOAT, nil)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
gl.BindTexture(gl.TEXTURE_2D, 0)
gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0+i, gl.TEXTURE_2D, buffer.colorAttachments[i], 0)
i++
// position
gl.GenTextures(1, &buffer.colorAttachments[i])
gl.BindTexture(gl.TEXTURE_2D, buffer.colorAttachments[i])
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGB, int32(width), int32(height), 0, gl.RGB, gl.FLOAT, nil)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
gl.BindTexture(gl.TEXTURE_2D, 0)
gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0+i, gl.TEXTURE_2D, buffer.colorAttachments[i], 0)
i++
if gl.CheckFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE { if gl.CheckFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE {
return buffer, errors.New("failed to create framebuffer") return buffer, errors.New("failed to create framebuffer")

View File

@ -1,7 +1,10 @@
package geometry package geometry
import ( import (
"errors"
"github.com/go-gl/gl/v4.6-core/gl" "github.com/go-gl/gl/v4.6-core/gl"
"github.com/go-gl/mathgl/mgl32"
"github.com/udhos/gwob" "github.com/udhos/gwob"
) )
@ -9,6 +12,7 @@ import (
// position 3 * float // position 3 * float
// texture 2 * float // texture 2 * float
// normal 3 * float // normal 3 * float
// tangent 3 * float
type Geometry struct { type Geometry struct {
vao uint32 vao uint32
@ -46,18 +50,28 @@ func new(coords []float32, indicies []int) (Geometry, error) {
gl.BindBuffer(gl.ARRAY_BUFFER, geometry.vbo) gl.BindBuffer(gl.ARRAY_BUFFER, geometry.vbo)
gl.BufferData(gl.ARRAY_BUFFER, len(coords)*4, gl.Ptr(coords), gl.STATIC_DRAW) gl.BufferData(gl.ARRAY_BUFFER, len(coords)*4, gl.Ptr(coords), gl.STATIC_DRAW)
var i uint32 = 0
// attributes // attributes
// position // position
gl.VertexAttribPointerWithOffset(0, 3, gl.FLOAT, false, 8*4, 0) gl.VertexAttribPointerWithOffset(i, 3, gl.FLOAT, false, 11*4, 0)
gl.EnableVertexAttribArray(0) gl.EnableVertexAttribArray(i)
i++
// texture // texture
gl.VertexAttribPointerWithOffset(1, 2, gl.FLOAT, false, 8*4, 3*4) gl.VertexAttribPointerWithOffset(i, 2, gl.FLOAT, false, 11*4, 3*4)
gl.EnableVertexAttribArray(1) gl.EnableVertexAttribArray(i)
i++
// normal // normal
gl.VertexAttribPointerWithOffset(2, 3, gl.FLOAT, false, 8*4, 5*4) gl.VertexAttribPointerWithOffset(2, 3, gl.FLOAT, false, 11*4, 5*4)
gl.EnableVertexAttribArray(2) gl.EnableVertexAttribArray(i)
i++
// tangent
gl.VertexAttribPointerWithOffset(i, 3, gl.FLOAT, false, 11*4, 8*4)
gl.EnableVertexAttribArray(i)
i++
// ebo // ebo
gl.GenBuffers(1, &geometry.ebo) gl.GenBuffers(1, &geometry.ebo)
@ -68,23 +82,85 @@ func new(coords []float32, indicies []int) (Geometry, error) {
return geometry, nil return geometry, nil
} }
func LoadOBJ(path string) (geometries Geometry, err error) { func LoadOBJ(path string) (geometry Geometry, err error) {
o, err := gwob.NewObjFromFile(path, &gwob.ObjParserOptions{}) o, err := gwob.NewObjFromFile(path, &gwob.ObjParserOptions{})
if err != nil { if err != nil {
return geometries, err return geometry, err
} }
return new(o.Coord, o.Indices) if !o.NormCoordFound {
return geometry, errors.New("missing normals in OBJ")
}
if !o.TextCoordFound {
return geometry, errors.New("missing texture UVs in OBJ")
}
// add tangent
coords := make([]float32, 0, len(o.Coord)*11)
tangents := make([][3]float32, len(o.Coord)/8)
// iterate faces
for i := 0; i < len(o.Indices); i += 3 {
i0 := o.Indices[i+0]
i1 := o.Indices[i+1]
i2 := o.Indices[i+2]
// positions
v0 := o.Coord[i0*8 : i0*8+3]
v1 := o.Coord[i1*8 : i1*8+3]
v2 := o.Coord[i2*8 : i2*8+3]
// UVs
uv0 := o.Coord[i0*8+3 : i0*8+5]
uv1 := o.Coord[i1*8+3 : i1*8+5]
uv2 := o.Coord[i2*8+3 : i2*8+5]
// edges
edge1 := [3]float32{v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2]}
edge2 := [3]float32{v2[0] - v0[0], v2[1] - v0[1], v2[2] - v0[2]}
// UV deltas
delta1 := [2]float32{uv1[0] - uv0[0], uv1[1] - uv0[1]}
delta2 := [2]float32{uv2[0] - uv0[0], uv2[1] - uv0[1]}
// calculate tangents
f := 1.0 / (delta1[0]*delta2[1] - delta2[0]*delta1[1])
tangent := [3]float32{
f * (delta2[1]*edge1[0] - delta1[1]*edge2[0]),
f * (delta2[1]*edge1[1] - delta1[1]*edge2[1]),
f * (delta2[1]*edge1[2] - delta1[1]*edge2[2]),
}
tangents[i0] = mgl32.Vec3(tangents[i0]).Add(tangent)
tangents[i1] = mgl32.Vec3(tangents[i1]).Add(tangent)
tangents[i2] = mgl32.Vec3(tangents[i2]).Add(tangent)
}
// build coords
for i := 0; i < len(o.Coord)/8; i++ {
pos := o.Coord[i*8 : i*8+3]
uv := o.Coord[i*8+3 : i*8+5]
norm := o.Coord[i*8+5 : i*8+8]
tan := mgl32.Vec3(tangents[i]).Normalize()
coords = append(coords, pos...)
coords = append(coords, uv...)
coords = append(coords, norm...)
coords = append(coords, tan[0:3]...)
}
return new(coords, o.Indices)
} }
func Screen() (Geometry, error) { func Screen() (Geometry, error) {
return new( return new(
[]float32{ []float32{
// xyz uv ijk // xyz, uv, nrm, tan
-1, -1, 0, 0, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, -1, 0, 1, 0, 0, 0, 0, 1, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0,
-1, 1, 0, 0, 1, 0, 0, 0, -1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0,
1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0,
}, },
[]int{ []int{
0, 1, 2, 0, 1, 2,