Implemented terrain texturing
This commit is contained in:
parent
1475fd2101
commit
9fdd47ea6e
19 changed files with 1977 additions and 87 deletions
|
|
@ -12,6 +12,8 @@ struct PointLight {
|
||||||
|
|
||||||
layout(location = 0) out vec4 outColor;
|
layout(location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
in vec4 gl_FragCoord;
|
||||||
|
|
||||||
layout(location = 2) in vec3 Normal;
|
layout(location = 2) in vec3 Normal;
|
||||||
layout(location = 3) in vec3 FragPos;
|
layout(location = 3) in vec3 FragPos;
|
||||||
layout(location = 4) in vec2 TexCoords;
|
layout(location = 4) in vec2 TexCoords;
|
||||||
|
|
@ -35,14 +37,19 @@ layout(push_constant) uniform pc {
|
||||||
int light_count;
|
int light_count;
|
||||||
} pushConstants;
|
} pushConstants;
|
||||||
|
|
||||||
vec3 calc_directional_light(vec3 normal, vec3 viewDir) {
|
layout (set = 1, binding = 1) uniform sampler2D sand;
|
||||||
|
layout (set = 1, binding = 2) uniform sampler2D grass;
|
||||||
|
layout (set = 1, binding = 3) uniform sampler2D rock;
|
||||||
|
|
||||||
|
vec3 calc_directional_light(vec3 normal, vec3 viewDir, vec3 diffuse) {
|
||||||
vec3 lightDir = normalize(-directional_light.direction);
|
vec3 lightDir = normalize(-directional_light.direction);
|
||||||
float diff = max(dot(normal, lightDir), 0.0);
|
float diff = max(dot(normal, lightDir), 0.0);
|
||||||
vec3 diffuse = directional_light.diffuse * diff;
|
vec3 ambient = directional_light.ambient * diffuse;
|
||||||
return (directional_light.ambient + diffuse);
|
vec3 d = directional_light.diffuse * diff * diffuse;
|
||||||
|
return (ambient + d);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 calc_point_light(int index, vec3 normal, vec3 fragPos, vec3 viewDir) {
|
vec3 calc_point_light(int index, vec3 normal, vec3 fragPos, vec3 viewDir, vec3 diffuse) {
|
||||||
float constant = point_lights.point_lights[index].data[0];
|
float constant = point_lights.point_lights[index].data[0];
|
||||||
float linear = point_lights.point_lights[index].data[1];
|
float linear = point_lights.point_lights[index].data[1];
|
||||||
float quadratic = point_lights.point_lights[index].data[2];
|
float quadratic = point_lights.point_lights[index].data[2];
|
||||||
|
|
@ -52,35 +59,43 @@ vec3 calc_point_light(int index, vec3 normal, vec3 fragPos, vec3 viewDir) {
|
||||||
vec3 reflectDir = reflect(-lightDir, normal);
|
vec3 reflectDir = reflect(-lightDir, normal);
|
||||||
float distance = length(point_lights.point_lights[index].position - fragPos);
|
float distance = length(point_lights.point_lights[index].position - fragPos);
|
||||||
float attenuation = 1.0 / (constant + linear * distance + quadratic * (distance * distance));
|
float attenuation = 1.0 / (constant + linear * distance + quadratic * (distance * distance));
|
||||||
vec3 ambient = point_lights.point_lights[index].ambient;
|
vec3 ambient = point_lights.point_lights[index].ambient * diffuse;
|
||||||
vec3 diffuse = point_lights.point_lights[index].diffuse * diff;
|
vec3 d = point_lights.point_lights[index].diffuse * diff * diffuse;
|
||||||
ambient *= attenuation;
|
ambient *= attenuation;
|
||||||
diffuse *= attenuation;
|
d *= attenuation;
|
||||||
return (ambient + diffuse);
|
return (ambient + d);
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec3 norm = normalize(Normal);
|
vec3 norm = normalize(Normal);
|
||||||
vec3 viewDir = normalize(viewPos.pos - FragPos);
|
vec3 viewDir = normalize(viewPos.pos - FragPos);
|
||||||
|
|
||||||
vec3 result = calc_directional_light(norm, viewDir);
|
|
||||||
|
|
||||||
|
float height = FragPos.y;
|
||||||
|
|
||||||
|
float sandWeight = 1.0 - smoothstep(0.0, 0.035, height);
|
||||||
|
float grassWeight = smoothstep(0.035, 0.15, height) - smoothstep(0.25, 0.4, height);
|
||||||
|
float rockWeight = smoothstep(0.25, 0.4, height);
|
||||||
|
|
||||||
|
float total = sandWeight + grassWeight + rockWeight;
|
||||||
|
sandWeight /= total;
|
||||||
|
grassWeight /= total;
|
||||||
|
rockWeight /= total;
|
||||||
|
|
||||||
|
vec4 sandColor = texture(sand, TexCoords);
|
||||||
|
vec4 grassColor = texture(grass, TexCoords);
|
||||||
|
vec4 rockColor = texture(rock, TexCoords);
|
||||||
|
|
||||||
|
vec4 finalColor = sandColor * sandWeight +
|
||||||
|
grassColor * grassWeight +
|
||||||
|
rockColor * rockWeight;
|
||||||
|
|
||||||
|
vec3 result = calc_directional_light(norm, viewDir, vec3(finalColor));
|
||||||
//vec3 result = vec3(0.0, 0.0, 0.0);
|
//vec3 result = vec3(0.0, 0.0, 0.0);
|
||||||
for(int i = 0; i < pushConstants.light_count; i++)
|
for(int i = 0; i < pushConstants.light_count; i++)
|
||||||
result += calc_point_light(i, norm, FragPos, viewDir);
|
result += calc_point_light(i, norm, FragPos, viewDir, vec3(finalColor));
|
||||||
|
|
||||||
vec3 tall = vec3(1.0, 0.0, 0.0);
|
outColor = vec4(result, 1.0);
|
||||||
vec3 mid = vec3(0.0, 1.0, 0.0);
|
//outColor = vec4(finalColor);
|
||||||
vec3 water = vec3(0.0, 0.0, 1.0);
|
|
||||||
|
|
||||||
vec3 color;
|
|
||||||
|
|
||||||
if (FragPos.y < 0.05) {
|
|
||||||
color = water;
|
|
||||||
} else if (FragPos.y > 0.3) {
|
|
||||||
color = tall;
|
|
||||||
} else {
|
|
||||||
color = mid;
|
|
||||||
}
|
|
||||||
|
|
||||||
outColor = vec4(color+result, 1.0);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
|
|
||||||
layout(location = 0) in vec3 vertPos;
|
layout(location = 0) in vec3 vertPos;
|
||||||
layout(location = 1) in vec3 normal;
|
layout(location = 1) in vec3 normal;
|
||||||
layout(location = 2) in vec2 uv;
|
layout(location = 2) in vec2 uv;
|
||||||
|
|
@ -19,17 +20,18 @@ layout(location = 4) out vec2 TexCoords;
|
||||||
layout (set = 1, binding = 0) uniform sampler2D diffuseSampler;
|
layout (set = 1, binding = 0) uniform sampler2D diffuseSampler;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
float texelSize = 1.0 / 200;
|
float texelSize = 1.0 / (50*4);
|
||||||
float hL = texture(diffuseSampler, uv - vec2(texelSize, 0.0)).r;
|
float hL = texture(diffuseSampler, uv - vec2(texelSize, 0.0)).r * 10;
|
||||||
float hR = texture(diffuseSampler, uv + vec2(texelSize, 0.0)).r;
|
float hR = texture(diffuseSampler, uv + vec2(texelSize, 0.0)).r * 10;
|
||||||
float hD = texture(diffuseSampler, uv - vec2(0.0, texelSize)).r;
|
float hD = texture(diffuseSampler, uv - vec2(0.0, texelSize)).r * 10;
|
||||||
float hU = texture(diffuseSampler, uv + vec2(0.0, texelSize)).r;
|
float hU = texture(diffuseSampler, uv + vec2(0.0, texelSize)).r * 10;
|
||||||
|
|
||||||
float dX = (hR - hL) * 15.0;
|
float dX = (hR - hL) * 15.0;
|
||||||
float dY = (hU - hD) * 15.0;
|
float dY = (hU - hD) * 15.0;
|
||||||
|
|
||||||
float y = texture(diffuseSampler, uv).x * 3;
|
float y = texture(diffuseSampler, uv).x;
|
||||||
vec4 out_vec = proj.proj * view.view * vec4(vec3(vertPos.x, y, vertPos.z), 1.0);
|
|
||||||
|
vec4 out_vec = proj.proj * view.view * vec4(vec3(vertPos.x, y * 10, vertPos.z), 1.0);
|
||||||
FragPos = vec3(vertPos.x, y, vertPos.z);
|
FragPos = vec3(vertPos.x, y, vertPos.z);
|
||||||
|
|
||||||
Normal = normalize(vec3(-dX, -dY, 1.0));
|
Normal = normalize(vec3(-dX, -dY, 1.0));
|
||||||
|
|
|
||||||
BIN
assets/textures/grass.png
Normal file
BIN
assets/textures/grass.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 MiB |
BIN
assets/textures/rock.png
Normal file
BIN
assets/textures/rock.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.5 MiB |
BIN
assets/textures/sand.png
Normal file
BIN
assets/textures/sand.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1 MiB |
BIN
colormap.png
Normal file
BIN
colormap.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 104 KiB |
2
ext/stb_image.c
vendored
2
ext/stb_image.c
vendored
|
|
@ -1,2 +1,4 @@
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
#include "stb_image.h"
|
#include "stb_image.h"
|
||||||
|
#include "stb_image_write.h"
|
||||||
|
|
|
||||||
1724
ext/stb_image_write.h
vendored
Normal file
1724
ext/stb_image_write.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
BIN
heightmap.png
Normal file
BIN
heightmap.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 414 KiB |
|
|
@ -279,3 +279,7 @@ pub fn normalize(a: @Vector(3, f32)) @Vector(3, f32) {
|
||||||
pub inline fn scaleVector(a: @Vector(3, f32), s: f32) @Vector(3, f32) {
|
pub inline fn scaleVector(a: @Vector(3, f32), s: f32) @Vector(3, f32) {
|
||||||
return a * @as(@Vector(3, f32), @splat(s));
|
return a * @as(@Vector(3, f32), @splat(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub inline fn inverseLerp(a: f64, b: f64, t: f64) f64 {
|
||||||
|
return (t - a) / (b - a);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,17 +62,16 @@ fn noise(self: Self, x: f64, y: f64) f64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fbm(self: Self, x: f64, y: f64, octaves: u32, lacunarity: f64, gain: f64) f64 {
|
pub fn fbm(self: Self, x: f64, y: f64, octaves: u32, lacunarity: f64, gain: f64) f64 {
|
||||||
var total: f64 = 0.0;
|
|
||||||
var amplitude: f64 = 1.0;
|
var amplitude: f64 = 1.0;
|
||||||
var frequency: f64 = 1.0;
|
var frequency: f64 = 1.0;
|
||||||
var maxAmplitude: f64 = 1.0;
|
var maxAmplitude: f64 = 0.0;
|
||||||
|
|
||||||
for(0..octaves) |_| {
|
for(0..octaves) |_| {
|
||||||
total += self.noise(x * frequency, y * frequency) * amplitude;
|
const value = (self.noise(x * frequency, y * frequency) * 2) - 1;
|
||||||
maxAmplitude += amplitude;
|
maxAmplitude += amplitude * value;
|
||||||
amplitude *= gain;
|
amplitude *= gain;
|
||||||
frequency *= lacunarity;
|
frequency *= lacunarity;
|
||||||
}
|
}
|
||||||
|
|
||||||
return total / maxAmplitude;
|
return maxAmplitude;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -216,7 +216,7 @@ pub fn bindDescriptorSets(self: Self, pipeline: vk.GraphicsPipeline, frame: usiz
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bindTerrainSets(self: Self, pipeline: vk.TerrainPipeline, frame: usize) void {
|
pub fn bindTerrainSets(self: Self, pipeline: vk.TerrainPipeline, frame: usize) void {
|
||||||
const sets = [_]c.VkDescriptorSet {pipeline.descriptor_set, pipeline.heightmap};
|
const sets = [_]c.VkDescriptorSet {pipeline.descriptor_set, pipeline.map};
|
||||||
c.vkCmdBindDescriptorSets(self.command_buffers[frame], c.VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout, 0, 2, sets[0..].ptr, 0, null);
|
c.vkCmdBindDescriptorSets(self.command_buffers[frame], c.VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout, 0, 2, sets[0..].ptr, 0, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -647,8 +647,8 @@ pub fn init(allocator: Allocator, device: vk.Device, swapchain: vk.Swapchain, re
|
||||||
.view_pos_memory = view_pos_data,
|
.view_pos_memory = view_pos_data,
|
||||||
.view_pos_buffer = view_pos_buffer,
|
.view_pos_buffer = view_pos_buffer,
|
||||||
.transform_buffer = transform_buffer,
|
.transform_buffer = transform_buffer,
|
||||||
.diffuse_sampler = try vk.Sampler.init(device),
|
.diffuse_sampler = try vk.Sampler.init(device, .linear),
|
||||||
.specular_sampler = try vk.Sampler.init(device),
|
.specular_sampler = try vk.Sampler.init(device, .linear),
|
||||||
.textures = std.ArrayList(c.VkDescriptorSet).init(allocator),
|
.textures = std.ArrayList(c.VkDescriptorSet).init(allocator),
|
||||||
.vertex_buffer = vertex_buffer,
|
.vertex_buffer = vertex_buffer,
|
||||||
.index_buffer = index_buffer,
|
.index_buffer = index_buffer,
|
||||||
|
|
|
||||||
|
|
@ -120,26 +120,29 @@ fn createIndexBuffer(device: vk.Device, indices: std.ArrayList(u32)) !vk.Buffer
|
||||||
return index_buffer;
|
return index_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn terrain(allocator: std.mem.Allocator, device: vk.Device, width: usize, height: usize, resolution: f32) !struct { vk.Buffer, vk.Buffer } {
|
pub fn terrain(allocator: std.mem.Allocator, device: vk.Device, width: usize, height: usize, resolution: usize) !struct { vk.Buffer, vk.Buffer } {
|
||||||
var vertices = std.ArrayList([8]f32).init(allocator);
|
var vertices = std.ArrayList([8]f32).init(allocator);
|
||||||
defer vertices.deinit();
|
defer vertices.deinit();
|
||||||
var indices = std.ArrayList(u32).init(allocator);
|
var indices = std.ArrayList(u32).init(allocator);
|
||||||
defer indices.deinit();
|
defer indices.deinit();
|
||||||
|
|
||||||
for (0..width) |x| {
|
for (0..width*resolution) |x| {
|
||||||
for (0..height) |z| {
|
for (0..height*resolution) |z| {
|
||||||
const vertex: [8]f32 = .{@as(f32, @floatFromInt(x))/resolution, 0.0, @as(f32, @floatFromInt(z))/resolution, 0.0, 0.0, 0.0, @as(f32, @floatFromInt(x)) / @as(f32, @floatFromInt(width)), @as(f32, @floatFromInt(z)) / @as(f32, @floatFromInt(height))};
|
const offset_x = @as(f32, @floatFromInt(x)) / @as(f32, @floatFromInt(width*resolution - 1)) * @as(f32, @floatFromInt(width));
|
||||||
|
const offset_z = @as(f32, @floatFromInt(z)) / @as(f32, @floatFromInt(height*resolution - 1)) * @as(f32, @floatFromInt(height));
|
||||||
|
|
||||||
|
const vertex: [8]f32 = .{offset_x, 0.0, offset_z, 0.0, 0.0, 0.0, @as(f32, @floatFromInt(x)) / @as(f32, @floatFromInt(width*resolution - 1)), @as(f32, @floatFromInt(z)) / @as(f32, @floatFromInt(height*resolution - 1))};
|
||||||
try vertices.append(vertex);
|
try vertices.append(vertex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (0..width-1) |x| {
|
for (0..width*resolution-1) |x| {
|
||||||
for (0..height-1) |z| {
|
for (0..height*resolution-1) |z| {
|
||||||
const top_left = @as(u32, @intCast(z * width + x));
|
const top_left = @as(u32, @intCast(z * width*resolution + x));
|
||||||
const top_right = @as(u32, @intCast(z * width + (x+1)));
|
const top_right = @as(u32, @intCast(z * width*resolution + (x+1)));
|
||||||
const bottom_left = @as(u32, @intCast((z+1) * width + x));
|
const bottom_left = @as(u32, @intCast((z+1) * width*resolution + x));
|
||||||
const bottom_right = @as(u32, @intCast((z+1) * width + (x + 1)));
|
const bottom_right = @as(u32, @intCast((z+1) * width*resolution + (x + 1)));
|
||||||
|
|
||||||
try indices.append(top_left);
|
try indices.append(top_left);
|
||||||
try indices.append(top_right);
|
try indices.append(top_right);
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ const std = @import("std");
|
||||||
const vk = @import("vulkan.zig");
|
const vk = @import("vulkan.zig");
|
||||||
const Mesh = @import("Mesh.zig");
|
const Mesh = @import("Mesh.zig");
|
||||||
const math = @import("math");
|
const math = @import("math");
|
||||||
|
const stb = vk.Texture.stb;
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub const Generator = struct {
|
pub const Generator = struct {
|
||||||
|
|
@ -15,7 +16,7 @@ pub const Generator = struct {
|
||||||
width: usize,
|
width: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
seed: u64,
|
seed: u64,
|
||||||
resolution: f32 = 1.0,
|
resolution: usize = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
heightmap: []f64,
|
heightmap: []f64,
|
||||||
|
|
@ -27,25 +28,56 @@ texture: vk.Texture,
|
||||||
vertex_buffer: vk.Buffer,
|
vertex_buffer: vk.Buffer,
|
||||||
index_buffer: vk.Buffer,
|
index_buffer: vk.Buffer,
|
||||||
|
|
||||||
|
const layers: [5][3]u32 = .{
|
||||||
|
.{0, 94, 255},
|
||||||
|
.{222, 208, 20},
|
||||||
|
.{14, 122, 41},
|
||||||
|
.{64, 20, 20},
|
||||||
|
.{253, 253, 253},
|
||||||
|
};
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator, device: vk.Device, generator: Generator) !Self {
|
pub fn init(allocator: std.mem.Allocator, device: vk.Device, generator: Generator) !Self {
|
||||||
const perlin: math.PerlinNoise = .{ .seed = generator.seed };
|
const perlin: math.PerlinNoise = .{ .seed = generator.seed };
|
||||||
const heightmap = try allocator.alloc(f64, generator.width * generator.height);
|
const heightmap = try allocator.alloc(f64, generator.width*generator.resolution * generator.height*generator.resolution);
|
||||||
const heightmap_data = try allocator.alloc(u32, generator.width * generator.height);
|
const heightmap_data = try allocator.alloc(u32, generator.width*generator.resolution * generator.height*generator.resolution);
|
||||||
defer allocator.free(heightmap_data);
|
defer allocator.free(heightmap_data);
|
||||||
for (0..generator.width) |x| {
|
|
||||||
for (0..generator.height) |y| {
|
|
||||||
var pixel = (perlin.fbm(@as(f64, @floatFromInt(x)) * generator.scale, @as(f64, @floatFromInt(y)) * generator.scale, generator.octaves, generator.lacunarity, generator.gain) * generator.multiplier);
|
|
||||||
pixel = std.math.pow(f64, pixel, generator.exponent);
|
|
||||||
const gray: u32 = @intFromFloat(pixel * 255);
|
|
||||||
const color: u32 = (255 << 24) | (gray << 16) | (gray << 8) | gray;
|
|
||||||
|
|
||||||
heightmap[x*generator.width + y] = pixel;
|
var max_noise_height = std.math.floatMin(f64);
|
||||||
heightmap_data[x*generator.width + y] = color;
|
var min_noise_height = std.math.floatMax(f64);
|
||||||
|
|
||||||
|
const columns = generator.width*generator.resolution;
|
||||||
|
const rows = generator.height*generator.resolution;
|
||||||
|
|
||||||
|
for (0..columns) |x| {
|
||||||
|
for (0..rows) |y| {
|
||||||
|
const u = @as(f64, @floatFromInt(x)) / @as(f64, @floatFromInt(columns - 1));
|
||||||
|
const v = @as(f64, @floatFromInt(y)) / @as(f64, @floatFromInt(rows - 1));
|
||||||
|
|
||||||
|
const h_x = u * @as(f64, @floatFromInt(generator.width));
|
||||||
|
const h_y = v * @as(f64, @floatFromInt(generator.height));
|
||||||
|
|
||||||
|
var pixel = (perlin.fbm(h_x / generator.scale, h_y / generator.scale, generator.octaves, generator.lacunarity, generator.gain) * generator.multiplier);
|
||||||
|
|
||||||
|
if (pixel > max_noise_height) {
|
||||||
|
max_noise_height = pixel;
|
||||||
|
} else if (pixel < min_noise_height) {
|
||||||
|
min_noise_height = pixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixel = std.math.pow(f64, pixel, generator.exponent);
|
||||||
|
pixel = math.inverseLerp(min_noise_height, max_noise_height, pixel);
|
||||||
|
|
||||||
|
heightmap[x*columns + y] = pixel;
|
||||||
|
|
||||||
|
const gray: u32 = @intFromFloat(pixel * 255);
|
||||||
|
const grayscale: u32 = (255 << 24) | (gray << 16) | (gray << 8) | gray;
|
||||||
|
|
||||||
|
heightmap_data[x*columns + y] = grayscale;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const vertex_buffer, const index_buffer = try Mesh.terrain(allocator, device, generator.width, generator.height, generator.resolution);
|
const vertex_buffer, const index_buffer = try Mesh.terrain(allocator, device, generator.width, generator.height, generator.resolution);
|
||||||
const heightmap_texture = try vk.Texture.fromBytes(@alignCast(@ptrCast(heightmap_data)), device, generator.width, generator.height);
|
const heightmap_texture = try vk.Texture.fromBytes(@alignCast(@ptrCast(heightmap_data)), device, generator.width*generator.resolution, generator.height*generator.resolution);
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.heightmap = heightmap,
|
.heightmap = heightmap,
|
||||||
|
|
@ -68,3 +100,21 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator, device: vk.Device) !voi
|
||||||
pub fn getHeight(self: Self, x: usize, y: usize) f64 {
|
pub fn getHeight(self: Self, x: usize, y: usize) f64 {
|
||||||
return self.heightmap[x*self.width + y];
|
return self.heightmap[x*self.width + y];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pixelToNoise(
|
||||||
|
x: usize,
|
||||||
|
y: usize,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
world_min: @Vector(2, f64),
|
||||||
|
world_max: @Vector(2, f64),
|
||||||
|
scale: f64
|
||||||
|
) struct { f64, f64 }{
|
||||||
|
const u = (@as(f64, @floatFromInt(x)) + 0.5) / @as(f64, @floatFromInt(width));
|
||||||
|
const v = (@as(f64, @floatFromInt(y)) + 0.5) / @as(f64, @floatFromInt(height));
|
||||||
|
|
||||||
|
const world_x = world_min[0] + u * (world_max[0] - world_min[0]);
|
||||||
|
const world_y = world_min[1] + v * (world_max[1] - world_min[1]);
|
||||||
|
|
||||||
|
return .{ world_x / scale, world_y / scale };
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,10 @@ descriptor_pool: c.VkDescriptorPool,
|
||||||
descriptor_set: c.VkDescriptorSet,
|
descriptor_set: c.VkDescriptorSet,
|
||||||
descriptor_set_layout: c.VkDescriptorSetLayout,
|
descriptor_set_layout: c.VkDescriptorSetLayout,
|
||||||
heightmap_sampler: vk.Sampler,
|
heightmap_sampler: vk.Sampler,
|
||||||
heightmap: c.VkDescriptorSet,
|
sand_sampler: vk.Sampler,
|
||||||
|
grass_sampler: vk.Sampler,
|
||||||
|
rock_sampler: vk.Sampler,
|
||||||
|
map: c.VkDescriptorSet,
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
|
|
@ -172,8 +175,29 @@ pub fn init(
|
||||||
.stageFlags = c.VK_SHADER_STAGE_VERTEX_BIT,
|
.stageFlags = c.VK_SHADER_STAGE_VERTEX_BIT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const sand_sampler_binding = c.VkDescriptorSetLayoutBinding{
|
||||||
|
.binding = 1,
|
||||||
|
.descriptorType = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.stageFlags = c.VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||||
|
};
|
||||||
|
|
||||||
|
const grass_sampler_binding = c.VkDescriptorSetLayoutBinding{
|
||||||
|
.binding = 2,
|
||||||
|
.descriptorType = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.stageFlags = c.VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||||
|
};
|
||||||
|
|
||||||
|
const stone_sampler_binding = c.VkDescriptorSetLayoutBinding{
|
||||||
|
.binding = 3,
|
||||||
|
.descriptorType = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.stageFlags = c.VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||||
|
};
|
||||||
|
|
||||||
const bindings = [_]c.VkDescriptorSetLayoutBinding{projection_binding, view_binding, directional_light_binding, point_lights_binding, view_pos_binding};
|
const bindings = [_]c.VkDescriptorSetLayoutBinding{projection_binding, view_binding, directional_light_binding, point_lights_binding, view_pos_binding};
|
||||||
const texture_bindings = [_]c.VkDescriptorSetLayoutBinding{heightmap_sampler_binding};
|
const texture_bindings = [_]c.VkDescriptorSetLayoutBinding{heightmap_sampler_binding, sand_sampler_binding, grass_sampler_binding, stone_sampler_binding};
|
||||||
|
|
||||||
var descriptor_set_layout: c.VkDescriptorSetLayout = undefined;
|
var descriptor_set_layout: c.VkDescriptorSetLayout = undefined;
|
||||||
var texture_descriptor_set_layout: c.VkDescriptorSetLayout = undefined;
|
var texture_descriptor_set_layout: c.VkDescriptorSetLayout = undefined;
|
||||||
|
|
@ -186,7 +210,7 @@ pub fn init(
|
||||||
|
|
||||||
const texture_descriptor_set_layout_info = c.VkDescriptorSetLayoutCreateInfo{
|
const texture_descriptor_set_layout_info = c.VkDescriptorSetLayoutCreateInfo{
|
||||||
.sType = c.VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
.sType = c.VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||||
.bindingCount = 1,
|
.bindingCount = 4,
|
||||||
.pBindings = texture_bindings[0..].ptr,
|
.pBindings = texture_bindings[0..].ptr,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -256,7 +280,7 @@ pub fn init(
|
||||||
|
|
||||||
const sampler_size = c.VkDescriptorPoolSize{
|
const sampler_size = c.VkDescriptorPoolSize{
|
||||||
.type = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
.type = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||||
.descriptorCount = 1,
|
.descriptorCount = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
const sizes = [_]c.VkDescriptorPoolSize {size, sampler_size};
|
const sizes = [_]c.VkDescriptorPoolSize {size, sampler_size};
|
||||||
|
|
@ -379,12 +403,15 @@ pub fn init(
|
||||||
.descriptor_pool = descriptor_pool,
|
.descriptor_pool = descriptor_pool,
|
||||||
.descriptor_set = descriptor_set,
|
.descriptor_set = descriptor_set,
|
||||||
.descriptor_set_layout = descriptor_set_layout,
|
.descriptor_set_layout = descriptor_set_layout,
|
||||||
.heightmap_sampler = try vk.Sampler.init(device),
|
.heightmap_sampler = try vk.Sampler.init(device, .nearest),
|
||||||
.heightmap = undefined,
|
.sand_sampler = try vk.Sampler.init(device, .linear),
|
||||||
|
.grass_sampler = try vk.Sampler.init(device, .linear),
|
||||||
|
.rock_sampler = try vk.Sampler.init(device, .linear),
|
||||||
|
.map = undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setHeightmap(self: *Self, device: anytype, heightmap: Texture) !void {
|
pub fn setMaps(self: *Self, device: anytype, heightmap: Texture) !void {
|
||||||
var set_layouts = [_]c.VkDescriptorSetLayout{self.texture_set_layout};
|
var set_layouts = [_]c.VkDescriptorSetLayout{self.texture_set_layout};
|
||||||
const descriptor_allocate_info = c.VkDescriptorSetAllocateInfo{
|
const descriptor_allocate_info = c.VkDescriptorSetAllocateInfo{
|
||||||
.sType = c.VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
.sType = c.VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||||
|
|
@ -396,25 +423,79 @@ pub fn setHeightmap(self: *Self, device: anytype, heightmap: Texture) !void {
|
||||||
var descriptor_set: c.VkDescriptorSet = undefined;
|
var descriptor_set: c.VkDescriptorSet = undefined;
|
||||||
try vk.mapError(c.vkAllocateDescriptorSets(device.handle, &descriptor_allocate_info, &descriptor_set));
|
try vk.mapError(c.vkAllocateDescriptorSets(device.handle, &descriptor_allocate_info, &descriptor_set));
|
||||||
|
|
||||||
const texture_info: c.VkDescriptorImageInfo = .{
|
const height_info: c.VkDescriptorImageInfo = .{
|
||||||
.imageLayout = c.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
.imageLayout = c.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||||
.imageView = heightmap.image_view,
|
.imageView = heightmap.image_view,
|
||||||
.sampler = self.heightmap_sampler.handle,
|
.sampler = self.heightmap_sampler.handle,
|
||||||
};
|
};
|
||||||
|
|
||||||
const write_texture_descriptor_set = c.VkWriteDescriptorSet{
|
const sand = try Texture.init("assets/textures/sand.png", device);
|
||||||
|
const grass = try Texture.init("assets/textures/grass.png", device);
|
||||||
|
const rock = try Texture.init("assets/textures/rock.png", device);
|
||||||
|
|
||||||
|
const sand_info: c.VkDescriptorImageInfo = .{
|
||||||
|
.imageLayout = c.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||||
|
.imageView = sand.image_view,
|
||||||
|
.sampler = self.sand_sampler.handle,
|
||||||
|
};
|
||||||
|
|
||||||
|
const grass_info: c.VkDescriptorImageInfo = .{
|
||||||
|
.imageLayout = c.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||||
|
.imageView = grass.image_view,
|
||||||
|
.sampler = self.grass_sampler.handle,
|
||||||
|
};
|
||||||
|
|
||||||
|
const rock_info: c.VkDescriptorImageInfo = .{
|
||||||
|
.imageLayout = c.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||||
|
.imageView = rock.image_view,
|
||||||
|
.sampler = self.rock_sampler.handle,
|
||||||
|
};
|
||||||
|
|
||||||
|
const write_height_descriptor_set = c.VkWriteDescriptorSet{
|
||||||
.sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
.sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||||
.dstSet = descriptor_set,
|
.dstSet = descriptor_set,
|
||||||
.dstBinding = 0,
|
.dstBinding = 0,
|
||||||
.dstArrayElement = 0,
|
.dstArrayElement = 0,
|
||||||
.descriptorCount = 1,
|
.descriptorCount = 1,
|
||||||
.descriptorType = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
.descriptorType = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||||
.pImageInfo = &texture_info,
|
.pImageInfo = &height_info,
|
||||||
};
|
};
|
||||||
|
|
||||||
c.vkUpdateDescriptorSets(device.handle, 1, &write_texture_descriptor_set, 0, null);
|
const write_sand_descriptor_set = c.VkWriteDescriptorSet{
|
||||||
|
.sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||||
|
.dstSet = descriptor_set,
|
||||||
|
.dstBinding = 1,
|
||||||
|
.dstArrayElement = 0,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||||
|
.pImageInfo = &sand_info,
|
||||||
|
};
|
||||||
|
|
||||||
self.heightmap = descriptor_set;
|
const write_grass_descriptor_set = c.VkWriteDescriptorSet{
|
||||||
|
.sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||||
|
.dstSet = descriptor_set,
|
||||||
|
.dstBinding = 2,
|
||||||
|
.dstArrayElement = 0,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||||
|
.pImageInfo = &grass_info,
|
||||||
|
};
|
||||||
|
|
||||||
|
const write_rock_descriptor_set = c.VkWriteDescriptorSet{
|
||||||
|
.sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||||
|
.dstSet = descriptor_set,
|
||||||
|
.dstBinding = 3,
|
||||||
|
.dstArrayElement = 0,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = c.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||||
|
.pImageInfo = &rock_info,
|
||||||
|
};
|
||||||
|
|
||||||
|
const writes = [_]c.VkWriteDescriptorSet {write_height_descriptor_set, write_sand_descriptor_set, write_grass_descriptor_set, write_rock_descriptor_set};
|
||||||
|
|
||||||
|
c.vkUpdateDescriptorSets(device.handle, 4, writes[0..].ptr, 0, null);
|
||||||
|
|
||||||
|
self.map = descriptor_set;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bind(self: Self, device: vk.Device, frame: usize) void {
|
pub fn bind(self: Self, device: vk.Device, frame: usize) void {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ const vk = @import("vulkan.zig");
|
||||||
const c = vk.c;
|
const c = vk.c;
|
||||||
pub const stb = @cImport({
|
pub const stb = @cImport({
|
||||||
@cInclude("stb_image.h");
|
@cInclude("stb_image.h");
|
||||||
|
@cInclude("stb_image_write.h");
|
||||||
});
|
});
|
||||||
|
|
||||||
image: c.VkImage,
|
image: c.VkImage,
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ const validation_layers: []const [*c]const u8 = if (!debug) &[0][*c]const u8{} e
|
||||||
pub const Error = error{
|
pub const Error = error{
|
||||||
out_of_host_memory,
|
out_of_host_memory,
|
||||||
out_of_device_memory,
|
out_of_device_memory,
|
||||||
|
out_of_pool_memory,
|
||||||
|
fragmented_pool,
|
||||||
initialization_failed,
|
initialization_failed,
|
||||||
layer_not_present,
|
layer_not_present,
|
||||||
extension_not_present,
|
extension_not_present,
|
||||||
|
|
@ -50,6 +52,8 @@ pub fn mapError(result: c_int) !void {
|
||||||
c.VK_ERROR_LAYER_NOT_PRESENT => Error.layer_not_present,
|
c.VK_ERROR_LAYER_NOT_PRESENT => Error.layer_not_present,
|
||||||
c.VK_ERROR_EXTENSION_NOT_PRESENT => Error.extension_not_present,
|
c.VK_ERROR_EXTENSION_NOT_PRESENT => Error.extension_not_present,
|
||||||
c.VK_ERROR_INCOMPATIBLE_DRIVER => Error.incompatible_driver,
|
c.VK_ERROR_INCOMPATIBLE_DRIVER => Error.incompatible_driver,
|
||||||
|
c.VK_ERROR_OUT_OF_POOL_MEMORY => Error.out_of_pool_memory,
|
||||||
|
c.VK_ERROR_FRAGMENTED_POOL => Error.fragmented_pool,
|
||||||
else => Error.unknown_error,
|
else => Error.unknown_error,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -105,16 +109,21 @@ pub const Buffer = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const SamplerType = enum {
|
||||||
|
linear,
|
||||||
|
nearest,
|
||||||
|
};
|
||||||
|
|
||||||
pub const Sampler = struct {
|
pub const Sampler = struct {
|
||||||
handle: c.VkSampler,
|
handle: c.VkSampler,
|
||||||
|
|
||||||
pub fn init(device: anytype) !Sampler {
|
pub fn init(device: anytype, filter: SamplerType) !Sampler {
|
||||||
var sampler: c.VkSampler = undefined;
|
var sampler: c.VkSampler = undefined;
|
||||||
|
|
||||||
const create_info: c.VkSamplerCreateInfo = .{
|
const create_info: c.VkSamplerCreateInfo = .{
|
||||||
.sType = c.VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
.sType = c.VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||||
.magFilter = c.VK_FILTER_NEAREST,
|
.magFilter = if (filter == .linear) c.VK_FILTER_LINEAR else c.VK_FILTER_NEAREST,
|
||||||
.minFilter = c.VK_FILTER_NEAREST,
|
.minFilter = if (filter == .linear) c.VK_FILTER_LINEAR else c.VK_FILTER_NEAREST,
|
||||||
.addressModeU = c.VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
.addressModeU = c.VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||||||
.addressModeV = c.VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
.addressModeV = c.VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||||||
.addressModeW = c.VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
.addressModeW = c.VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
||||||
|
|
|
||||||
|
|
@ -146,20 +146,20 @@ export fn sideros_init(init: api.GameInit) callconv(.c) void {
|
||||||
renderer = Renderer.init(allocator, @ptrCast(init.instance), @ptrCast(init.surface)) catch @panic("TODO: Gracefully handle error");
|
renderer = Renderer.init(allocator, @ptrCast(init.instance), @ptrCast(init.surface)) catch @panic("TODO: Gracefully handle error");
|
||||||
|
|
||||||
resources.terrain = rendering.Terrain.init(allocator, renderer.device, .{
|
resources.terrain = rendering.Terrain.init(allocator, renderer.device, .{
|
||||||
.octaves = 8,
|
.octaves = 4,
|
||||||
.lacunarity = 3.0,
|
.lacunarity = 2.0,
|
||||||
.gain = 0.5,
|
.gain = 0.5,
|
||||||
.scale = 0.01,
|
.scale = 30,
|
||||||
.multiplier = 3.0,
|
.multiplier = 1.0,
|
||||||
.exponent = 1.2,
|
.exponent = 1.0,
|
||||||
|
|
||||||
.width = 700,
|
.width = 100,
|
||||||
.height = 700,
|
.height = 100,
|
||||||
.seed = 12345678,
|
.seed = 2497852058242342,
|
||||||
.resolution = 10.0,
|
.resolution = 1,
|
||||||
}) catch @panic("TODO: handle this");
|
}) catch @panic("TODO: handle this");
|
||||||
|
|
||||||
renderer.terrain_pipeline.setHeightmap(renderer.device, resources.terrain.texture) catch @panic("TODO: handle this");
|
renderer.terrain_pipeline.setMaps(renderer.device, resources.terrain.texture) catch @panic("TODO: handle this");
|
||||||
|
|
||||||
pool.addSystemGroup(&[_]ecs.System{systems.render, systems.moveCamera}, true) catch @panic("TODO: Gracefuly handle error");
|
pool.addSystemGroup(&[_]ecs.System{systems.render, systems.moveCamera}, true) catch @panic("TODO: Gracefuly handle error");
|
||||||
pool.resources.renderer = &renderer;
|
pool.resources.renderer = &renderer;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue