mirror of
https://github.com/Drezil/imgui.git
synced 2024-11-22 11:57:00 +00:00
Shadows: Convex shape shadow improvement/fixes
Fixed pixel cracking on convex shadow edges Added convex shadow offset support Fixed some convex shadow fringing issues Added convex shadow demo code # Conflicts: # imgui_demo.cpp
This commit is contained in:
parent
01078a6077
commit
cc7387f680
161
imgui_demo.cpp
161
imgui_demo.cpp
@ -7618,47 +7618,180 @@ static void ShowExampleAppCustomRendering(bool* p_open)
|
||||
if (ImGui::BeginTabItem("Shadows"))
|
||||
{
|
||||
static float shadow_thickness = 40.0f;
|
||||
static bool shadow_filled = false;
|
||||
static ImVec4 shadow_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
static ImVec4 shape_color = ImVec4(0.9f, 0.0f, 0.0f, 1.0f);
|
||||
static bool shadow_filled = false;
|
||||
static ImVec4 shape_color = ImVec4(0.9f, 0.6f, 0.3f, 1.0f);
|
||||
static float shape_rounding = 0.0f;
|
||||
static ImVec4 bg_color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
static ImVec2 shadow_offset(0.0f, 0.0f);
|
||||
static ImVec4 background_color = ImVec4(0.5f, 0.5f, 0.7f, 1.0f);
|
||||
static bool wireframe = false;
|
||||
static bool aa = true;
|
||||
static int poly_shape_index = 0;
|
||||
ImGui::Checkbox("Shadow filled", &shadow_filled);
|
||||
ImGui::SameLine();
|
||||
HelpMarker("This will fill the section behind the shape to shadow. It's often unnecessary and wasteful but provided for consistency.");
|
||||
ImGui::Checkbox("Wireframe shapes", &wireframe);
|
||||
ImGui::SameLine();
|
||||
HelpMarker("This draws the shapes in wireframe so you can see the shadow underneath.");
|
||||
ImGui::Checkbox("Anti-aliasing", &aa);
|
||||
|
||||
ImGui::DragFloat("Shadow Thickness", &shadow_thickness, 1.0f, 0.0f, 100.0f, "%.02f");
|
||||
ImGui::SliderFloat2("Offset", (float*)&shadow_offset, -32.0f, 32.0f);
|
||||
ImGui::SameLine();
|
||||
HelpMarker("Note that currently circles/convex shapes do not support non-zero offsets for unfilled shadows.");
|
||||
ImGui::ColorEdit4("Background Color", &background_color.x);
|
||||
ImGui::ColorEdit4("Shadow Color", &shadow_color.x);
|
||||
ImGui::ColorEdit4("Shape Color", &shape_color.x);
|
||||
ImGui::DragFloat("Shape Rounding", &shape_rounding, 1.0f, 0.0f, 20.0f, "%.02f");
|
||||
ImGui::ColorEdit4("Background Color", &bg_color.x);
|
||||
ImGui::Combo("Convex shape", &poly_shape_index, "Shape 1\0Shape 2\0Shape 3\0Shape 4\0Shape 4 (winding reversed)");
|
||||
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
ImDrawListFlags old_flags = draw_list->Flags;
|
||||
|
||||
if (aa)
|
||||
draw_list->Flags |= ~ImDrawListFlags_AntiAliasedFill;
|
||||
else
|
||||
draw_list->Flags &= ~ImDrawListFlags_AntiAliasedFill;
|
||||
|
||||
// Fill a strip of background
|
||||
draw_list->AddRectFilled(ImVec2(ImGui::GetCursorScreenPos().x, ImGui::GetCursorScreenPos().y), ImVec2(ImGui::GetCursorScreenPos().x + ImGui::GetWindowContentRegionMax().x, ImGui::GetCursorScreenPos().y + 200.0f), ImGui::GetColorU32(background_color));
|
||||
|
||||
// Rectangle
|
||||
{
|
||||
ImVec2 p = ImGui::GetCursorScreenPos();
|
||||
ImVec2 r1(p.x + 50.0f, p.y + 50.0f);
|
||||
ImVec2 r2(p.x + 150.0f, p.y + 150.0f);
|
||||
draw_list->AddRectFilled(p, ImVec2(p.x + 200.0f, p.y + 200.0f), ImGui::GetColorU32(bg_color));
|
||||
if (shadow_filled)
|
||||
draw_list->AddShadowRectFilled(r1, r2, shadow_thickness, ImVec2(0.0f, 0.0f), ImGui::GetColorU32(shadow_color));
|
||||
draw_list->AddShadowRectFilled(r1, r2, shadow_thickness, shadow_offset, ImGui::GetColorU32(shadow_color));
|
||||
else
|
||||
draw_list->AddShadowRect(r1, r2, shadow_thickness, ImVec2(0.0f, 0.0f), ImGui::GetColorU32(shadow_color), shape_rounding);
|
||||
draw_list->AddRectFilled(r1, r2, ImGui::GetColorU32(shape_color), shape_rounding);
|
||||
draw_list->AddShadowRect(r1, r2, shadow_thickness, shadow_offset, ImGui::GetColorU32(shadow_color), shape_rounding);
|
||||
if (wireframe)
|
||||
draw_list->AddRect(r1, r2, ImGui::GetColorU32(shape_color), shape_rounding);
|
||||
else
|
||||
draw_list->AddRectFilled(r1, r2, ImGui::GetColorU32(shape_color), shape_rounding);
|
||||
ImGui::Dummy(ImVec2(200.0f, 200.0f));
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
// Circle
|
||||
{
|
||||
ImVec2 p = ImGui::GetCursorScreenPos();
|
||||
ImVec2 center(p.x + 100.0f, p.y + 100.0f);
|
||||
float radius = 50.0f;
|
||||
draw_list->AddRectFilled(p, ImVec2(p.x + 200.0f, p.y + 200.0f), ImGui::GetColorU32(bg_color));
|
||||
float off = 10.0f;
|
||||
ImVec2 r1(p.x + 50.0f + off, p.y + 50.0f + off);
|
||||
ImVec2 r2(p.x + 150.0f - off, p.y + 150.0f - off);
|
||||
ImVec2 c(p.x + 100.0f, p.y + 100.0f);
|
||||
if (shadow_filled)
|
||||
draw_list->AddShadowCircleFilled(center, radius, shadow_thickness, ImVec2(0.0f, 0.0f), ImGui::GetColorU32(shadow_color), 0);
|
||||
draw_list->AddShadowCircleFilled(c, 50.0f, shadow_thickness, shadow_offset, ImGui::GetColorU32(shadow_color));
|
||||
else
|
||||
draw_list->AddShadowCircle(center, radius, shadow_thickness, ImVec2(0.0f, 0.0f), ImGui::GetColorU32(shadow_color), 0);
|
||||
draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32(shape_color), 0);
|
||||
draw_list->AddShadowCircle(c, 50.0f, shadow_thickness, ImVec2(0.0f, 0.0f), ImGui::GetColorU32(shadow_color)); // Offset forced to zero here because it isn't supported
|
||||
if (wireframe)
|
||||
draw_list->AddCircle(c, 50.0f, ImGui::GetColorU32(shape_color), 0);
|
||||
else
|
||||
draw_list->AddCircleFilled(c, 50.0f, ImGui::GetColorU32(shape_color), 0);
|
||||
ImGui::Dummy(ImVec2(200.0f, 200.0f));
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
// Convex shape
|
||||
{
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
|
||||
const ImVec2 poly_centre(pos.x + 50.0f, pos.y + 100.0f);
|
||||
ImVec2* poly_points;
|
||||
int num_poly_points;
|
||||
|
||||
switch (poly_shape_index)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
{
|
||||
ImVec2 poly_point_data[] =
|
||||
{
|
||||
ImVec2(poly_centre.x - 32.0f, poly_centre.y),
|
||||
ImVec2(poly_centre.x - 24.0f, poly_centre.y + 24.0f),
|
||||
ImVec2(poly_centre.x, poly_centre.y + 32.0f),
|
||||
ImVec2(poly_centre.x + 24.0f, poly_centre.y + 24.0f),
|
||||
ImVec2(poly_centre.x + 32.0f, poly_centre.y),
|
||||
ImVec2(poly_centre.x + 24.0f, poly_centre.y - 24.0f),
|
||||
ImVec2(poly_centre.x, poly_centre.y - 32.0f),
|
||||
ImVec2(poly_centre.x - 32.0f, poly_centre.y - 32.0f)
|
||||
};
|
||||
poly_points = poly_point_data;
|
||||
num_poly_points = 8;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
ImVec2 poly_point_data[] =
|
||||
{
|
||||
ImVec2(poly_centre.x + 40.0f, poly_centre.y - 20.0f),
|
||||
ImVec2(poly_centre.x, poly_centre.y + 32.0f),
|
||||
ImVec2(poly_centre.x - 24.0f, poly_centre.y - 32.0f)
|
||||
};
|
||||
poly_points = poly_point_data;
|
||||
num_poly_points = 3;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
ImVec2 poly_point_data[] =
|
||||
{
|
||||
ImVec2(poly_centre.x - 32.0f, poly_centre.y),
|
||||
ImVec2(poly_centre.x, poly_centre.y + 32.0f),
|
||||
ImVec2(poly_centre.x + 32.0f, poly_centre.y),
|
||||
ImVec2(poly_centre.x, poly_centre.y - 32.0f)
|
||||
};
|
||||
poly_points = poly_point_data;
|
||||
num_poly_points = 4;
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
ImVec2 poly_point_data[] =
|
||||
{
|
||||
ImVec2(poly_centre.x - 4.0f, poly_centre.y - 20.0f),
|
||||
ImVec2(poly_centre.x + 12.0f, poly_centre.y + 2.0f),
|
||||
ImVec2(poly_centre.x + 8.0f, poly_centre.y + 16.0f),
|
||||
ImVec2(poly_centre.x, poly_centre.y + 32.0f),
|
||||
ImVec2(poly_centre.x - 16.0f, poly_centre.y - 32.0f)
|
||||
};
|
||||
poly_points = poly_point_data;
|
||||
num_poly_points = 5;
|
||||
break;
|
||||
}
|
||||
case 4: // Same as test case 3 but with reversed winding
|
||||
{
|
||||
ImVec2 poly_point_data[] =
|
||||
{
|
||||
ImVec2(poly_centre.x - 16.0f, poly_centre.y - 32.0f),
|
||||
ImVec2(poly_centre.x, poly_centre.y + 32.0f),
|
||||
ImVec2(poly_centre.x + 8.0f, poly_centre.y + 16.0f),
|
||||
ImVec2(poly_centre.x + 12.0f, poly_centre.y + 2.0f),
|
||||
ImVec2(poly_centre.x - 4.0f, poly_centre.y - 20.0f)
|
||||
};
|
||||
poly_points = poly_point_data;
|
||||
num_poly_points = 5;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (shadow_filled)
|
||||
draw_list->AddShadowConvexPolyFilled(poly_points, num_poly_points, shadow_thickness, shadow_offset, ImGui::GetColorU32(shadow_color));
|
||||
else
|
||||
draw_list->AddShadowConvexPoly(poly_points, num_poly_points, shadow_thickness, ImVec2(0.0f, 0.0f), ImGui::GetColorU32(shadow_color)); // Offset forced to zero because it isn't supported
|
||||
|
||||
if (wireframe)
|
||||
draw_list->AddPolyline(poly_points, num_poly_points, ImGui::GetColorU32(shape_color), true, 1.0f);
|
||||
else
|
||||
draw_list->AddConvexPolyFilled(poly_points, num_poly_points, ImGui::GetColorU32(shape_color));
|
||||
|
||||
ImGui::Dummy(ImVec2(200.0f, 200.0f));
|
||||
}
|
||||
|
||||
draw_list->Flags = old_flags;
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
|
@ -2168,7 +2168,15 @@ void ImDrawList::AddShadowRect(const ImVec2& p_min, const ImVec2& p_max, float s
|
||||
// Add a shadow for a convex shape described by points and num_points
|
||||
static void AddShadowConvexShapeEx(ImDrawList* draw_list, const ImVec2* points, int num_points, float shadow_thickness, const ImVec2& offset, ImU32 col, bool is_filled)
|
||||
{
|
||||
IM_UNUSED(offset); // Offset not currently supported
|
||||
IM_ASSERT((is_filled || (ImLengthSqr(offset) < 0.00001f)) && "Drawing circle/convex shape shadows with no center fill and an offset is not currently supported");
|
||||
IM_ASSERT(num_points >= 3);
|
||||
|
||||
// Calculate poly vertex order
|
||||
int vertex_winding = (((points[0].x * (points[1].y - points[2].y)) + (points[1].x * (points[2].y - points[0].y)) + (points[2].x * (points[0].y - points[1].y))) < 0.0f) ? -1 : 1;
|
||||
|
||||
// If we're using anti-aliasing, then inset the shadow by 0.5 pixels to avoid unpleasant fringing artifacts
|
||||
const bool use_inset_distance = (draw_list->Flags & ImDrawListFlags_AntiAliasedFill) && (!is_filled);
|
||||
const float inset_distance = 0.5f;
|
||||
|
||||
const ImVec4 uvs = draw_list->_Data->ShadowRectUvs[9];
|
||||
|
||||
@ -2191,16 +2199,54 @@ static void AddShadowConvexShapeEx(ImDrawList* draw_list, const ImVec2* points,
|
||||
// Normalize a vector
|
||||
#define NORMALIZE(vec) ((vec) / ImLength((vec), 0.001f))
|
||||
|
||||
const int required_stack_mem = (num_edges * sizeof(ImVec2)) + (num_edges * sizeof(float));
|
||||
const ImU8* base_mem_for_normals_and_edges = (ImU8*)alloca(required_stack_mem);
|
||||
ImU8* mem_for_normals_and_edges = (ImU8*)base_mem_for_normals_and_edges;
|
||||
|
||||
// Calculate edge normals
|
||||
ImVec2* edge_normals = (ImVec2*)alloca(num_edges * sizeof(ImVec2));
|
||||
ImVec2* edge_normals = (ImVec2*)mem_for_normals_and_edges;
|
||||
mem_for_normals_and_edges += num_edges * sizeof(ImVec2);
|
||||
|
||||
for (int edge_index = 0; edge_index < num_edges; edge_index++)
|
||||
{
|
||||
ImVec2 edge_start = points[edge_index];
|
||||
ImVec2 edge_start = points[edge_index]; // No need to apply offset here because the normal is unaffected
|
||||
ImVec2 edge_end = points[(edge_index + 1) % num_edges];
|
||||
ImVec2 edge_normal = NORMALIZE(ImVec2(edge_end.y - edge_start.y, -(edge_end.x - edge_start.x)));
|
||||
|
||||
edge_normals[edge_index] = edge_normal;
|
||||
edge_normals[edge_index] = edge_normal * (float)vertex_winding; // Flip normals for reverse winding
|
||||
}
|
||||
|
||||
// Pre-calculate edge scales
|
||||
// We need to do this because we need the edge strips to have widths that match up with the corner sections, otherwise pixel cracking can occur along the boundaries
|
||||
float* edge_size_scales = (float*)mem_for_normals_and_edges;
|
||||
mem_for_normals_and_edges += num_edges * sizeof(float);
|
||||
IM_ASSERT_PARANOID(mem_for_normals_and_edges == (base_mem_for_normals_and_edges + required_stack_mem)); // Check we used exactly what we allocated
|
||||
|
||||
{
|
||||
ImVec2 prev_edge_normal = edge_normals[num_edges - 1];
|
||||
|
||||
for (int edge_index = 0; edge_index < num_edges; edge_index++)
|
||||
{
|
||||
ImVec2 edge_normal = edge_normals[edge_index];
|
||||
float cos_angle_coverage = ImDot(edge_normal, prev_edge_normal);
|
||||
|
||||
if (cos_angle_coverage < 0.999999f)
|
||||
{
|
||||
float angle_coverage = ImAcos(cos_angle_coverage);
|
||||
|
||||
// If we are covering more than 90 degrees we need an intermediate vertex to stop the required expansion tending towards infinity, and thus the effective angle will be halved
|
||||
if (cos_angle_coverage <= 0.0f)
|
||||
angle_coverage *= 0.5f;
|
||||
|
||||
edge_size_scales[edge_index] = 1.0f / ImCos(angle_coverage * 0.5f); // How much we need to expand our size by to avoid clipping the corner of the texture off
|
||||
}
|
||||
else
|
||||
{
|
||||
edge_size_scales[edge_index] = 1.0f; // No corner, thus default scale
|
||||
}
|
||||
|
||||
prev_edge_normal = edge_normal;
|
||||
}
|
||||
}
|
||||
|
||||
const int max_vertices = (4 + (3 * 2) + (is_filled ? 1 : 0)) * num_edges; // 4 vertices per edge plus 3*2 for potentially two corner triangles, plus one per vertex for fill
|
||||
@ -2210,14 +2256,22 @@ static void AddShadowConvexShapeEx(ImDrawList* draw_list, const ImVec2* points,
|
||||
ImDrawVert* vtx_write = draw_list->_VtxWritePtr;
|
||||
ImDrawIdx current_idx = (ImDrawIdx)draw_list->_VtxCurrentIdx;
|
||||
|
||||
//ImVec2 previous_edge_start = points[0];
|
||||
ImVec2 edge_start = points[0];
|
||||
ImVec2 previous_edge_start = points[0] + offset;
|
||||
ImVec2 prev_edge_normal = edge_normals[num_edges - 1];
|
||||
ImVec2 edge_start = points[0] + offset;
|
||||
|
||||
if (use_inset_distance)
|
||||
edge_start -= NORMALIZE(edge_normals[0] + prev_edge_normal) * inset_distance;
|
||||
|
||||
for (int edge_index = 0; edge_index < num_edges; edge_index++)
|
||||
{
|
||||
const ImVec2 edge_end = points[(edge_index + 1) % num_edges];
|
||||
{
|
||||
ImVec2 edge_end = points[(edge_index + 1) % num_edges] + offset;
|
||||
ImVec2 edge_normal = edge_normals[edge_index];
|
||||
const float size_scale_start = edge_size_scales[edge_index];
|
||||
const float size_scale_end = edge_size_scales[(edge_index + 1) % num_edges];
|
||||
|
||||
if (use_inset_distance)
|
||||
edge_end -= NORMALIZE(edge_normals[(edge_index + 1) % num_edges] + edge_normal) * inset_distance;
|
||||
|
||||
// Add corner section
|
||||
float cos_angle_coverage = ImDot(edge_normal, prev_edge_normal);
|
||||
@ -2241,14 +2295,12 @@ static void AddShadowConvexShapeEx(ImDrawList* draw_list, const ImVec2* points,
|
||||
|
||||
// Calculate UV for the section of the curved texture
|
||||
|
||||
float angle_coverage = ImAcos(cos_angle_coverage);
|
||||
float sin_angle_coverage = ImSin(angle_coverage);
|
||||
|
||||
float size_scale = 1.0f / ImCos(angle_coverage * 0.5f); // How much we need to expand our size by to avoid clipping the corner of the texture off
|
||||
const float angle_coverage = ImAcos(cos_angle_coverage);
|
||||
const float sin_angle_coverage = ImSin(angle_coverage);
|
||||
|
||||
ImVec2 edge_delta = solid_to_edge_delta_texels;
|
||||
|
||||
edge_delta *= size_scale;
|
||||
edge_delta *= size_scale_start;
|
||||
|
||||
ImVec2 rotated_edge_delta = ImVec2((edge_delta.x * cos_angle_coverage) + (edge_delta.y * sin_angle_coverage), (edge_delta.x * sin_angle_coverage) + (edge_delta.y * cos_angle_coverage));
|
||||
|
||||
@ -2261,7 +2313,7 @@ static void AddShadowConvexShapeEx(ImDrawList* draw_list, const ImVec2* points,
|
||||
ImVec2 expanded_edge_uv = solid_uv + edge_delta;
|
||||
ImVec2 other_edge_uv = solid_uv + rotated_edge_delta; // Rotated UV to encompass the necessary section of the curve
|
||||
|
||||
float expanded_thickness = shadow_thickness * size_scale;
|
||||
float expanded_thickness = shadow_thickness * size_scale_start;
|
||||
|
||||
// Add a triangle to fill the corner
|
||||
ImVec2 outer_edge_start = edge_start + (prev_edge_normal * expanded_thickness);
|
||||
@ -2285,14 +2337,17 @@ static void AddShadowConvexShapeEx(ImDrawList* draw_list, const ImVec2* points,
|
||||
|
||||
if (edge_length > 0.00001f) // Don't try and process degenerate edges
|
||||
{
|
||||
ImVec2 outer_edge_start = edge_start + (edge_normal * shadow_thickness);
|
||||
ImVec2 outer_edge_end = edge_end + (edge_normal * shadow_thickness);
|
||||
ImVec2 outer_edge_start = edge_start + (edge_normal * shadow_thickness * size_scale_start);
|
||||
ImVec2 outer_edge_end = edge_end + (edge_normal * shadow_thickness * size_scale_end);
|
||||
|
||||
ImVec2 scaled_edge_uv_start = solid_uv + ((edge_uv - solid_uv) * size_scale_start);
|
||||
ImVec2 scaled_edge_uv_end = solid_uv + ((edge_uv - solid_uv) * size_scale_end);
|
||||
|
||||
// Write vertices, inner first, then outer
|
||||
vtx_write->pos = edge_start; vtx_write->col = col; vtx_write->uv = solid_uv; vtx_write++;
|
||||
vtx_write->pos = edge_end; vtx_write->col = col; vtx_write->uv = solid_uv; vtx_write++;
|
||||
vtx_write->pos = outer_edge_end; vtx_write->col = col; vtx_write->uv = edge_uv; vtx_write++;
|
||||
vtx_write->pos = outer_edge_start; vtx_write->col = col; vtx_write->uv = edge_uv; vtx_write++;
|
||||
vtx_write->pos = outer_edge_end; vtx_write->col = col; vtx_write->uv = scaled_edge_uv_end; vtx_write++;
|
||||
vtx_write->pos = outer_edge_start; vtx_write->col = col; vtx_write->uv = scaled_edge_uv_start; vtx_write++;
|
||||
|
||||
*(idx_write++) = current_idx;
|
||||
*(idx_write++) = current_idx + 1;
|
||||
@ -2311,7 +2366,7 @@ static void AddShadowConvexShapeEx(ImDrawList* draw_list, const ImVec2* points,
|
||||
// Add vertices
|
||||
for (int edge_index = 0; edge_index < num_edges; edge_index++)
|
||||
{
|
||||
vtx_write->pos = points[edge_index]; vtx_write->col = col; vtx_write->uv = solid_uv; vtx_write++;
|
||||
vtx_write->pos = points[edge_index] + offset; vtx_write->col = col; vtx_write->uv = solid_uv; vtx_write++;
|
||||
}
|
||||
|
||||
// Add tris
|
||||
@ -3694,7 +3749,7 @@ static void ImFontAtlasBuildRenderShadowTexData(ImFontAtlas* atlas)
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
float dist = DistanceFromPoint(ImVec2((float)x, (float)y), center_point);
|
||||
float alpha = 1.0f - ImMin(ImMax((float)dist /*+ shadow_cfg->TexDistanceFieldOffset*/, 0.0f) / ImMax((float)shadow_cfg->TexCornerSize /*+ shadow_cfg->TexDistanceFieldOffset*/, 0.001f), 1.0f);
|
||||
float alpha = 1.0f - ImMin(ImMax((float)dist + shadow_cfg->TexDistanceFieldOffset, 0.0f) / ImMax((float)shadow_cfg->TexCornerSize + shadow_cfg->TexDistanceFieldOffset, 0.001f), 1.0f);
|
||||
alpha = ImPow(alpha, shadow_cfg->TexFalloffPower); // Apply power curve to give a nicer falloff
|
||||
tex_data[x + (y * size)] = alpha;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user