Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support premultipliedAlpha #115

Merged
merged 5 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions example/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ async function loadSpine(root: Entity, engine: Engine, resource) {
spineEntity.transform.setPosition(-25 + Math.random() * 50, -250, 0);
const spineAnimation = spineEntity.addComponent(SpineAnimationRenderer);
if (scene === 'physic') {
spineAnimation.premultipliedAlpha = true;
spineEntity.transform.setScale(0.5, 0.5, 0.5);
}
spineAnimation.resource = spineResource;
Expand Down
37 changes: 23 additions & 14 deletions src/SpineAnimationRenderer.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import { Skeleton, AnimationState, Physics, TrackEntry, AnimationStateData } from "@esotericsoftware/spine-core";
import { SpineGenerator } from "./SpineGenerator";
import { AnimationState, AnimationStateData, Physics, Skeleton, TrackEntry } from "@esotericsoftware/spine-core";
import {
assignmentClone,
BoundingBox,
Buffer,
Renderer,
BufferBindFlag,
BufferUsage,
deepClone,
Engine,
Entity,
ignoreClone,
IndexBufferBinding,
IndexFormat,
Material,
Engine,
BoundingBox,
Primitive,
Renderer,
SubPrimitive,
deepClone,
VertexBufferBinding,
VertexElement,
VertexElementFormat,
BufferBindFlag,
BufferUsage,
VertexBufferBinding,
IndexBufferBinding,
IndexFormat,
assignmentClone,
} from "@galacean/engine";
import { SpineGenerator } from "./SpineGenerator";
import { SpineMaterial } from "./SpineMaterial";
import { SpineResource } from "./loader/SpineResource";
import { getBlendMode } from "./util/BlendMode";
Expand Down Expand Up @@ -60,7 +60,16 @@ export class SpineAnimationRenderer extends Renderer {
*/
@assignmentClone
zSpacing = 0.01;


/**
* Whether to use premultiplied alpha mode for rendering.
* When enabled, vertex color values are multiplied by the alpha channel.
* @remarks
If this option is enabled, the Spine editor must export textures with "Premultiply Alpha" checked.
*/
@assignmentClone
premultipliedAlpha = false;

/**
* Default state for spine animation.
* Contains the default animation name to be played, whether this animation should loop, the default skin name.
Expand Down Expand Up @@ -455,5 +464,5 @@ export class SpineAnimationDefaultConfig {
* The name of the default skin @defaultValue `default`
*/
public skinName: string = "default"
) {}
) { }
}
75 changes: 39 additions & 36 deletions src/SpineGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,27 @@
import {
Texture2D,
SubPrimitive,
Vector3,
Material,
Engine,
BoundingBox,
} from "@galacean/engine";
import {
Skeleton,
SkeletonClipping,
RegionAttachment,
MeshAttachment,
ClippingAttachment,
ArrayLike,
Color,
BlendMode,
SkeletonData,
Skin,
ClippingAttachment,
Color,
MeshAttachment,
NumberArrayLike,
Attachment,
RegionAttachment,
Skeleton,
SkeletonClipping
} from "@esotericsoftware/spine-core";
import {
BoundingBox,
Engine,
Material,
SubPrimitive,
Texture2D,
Vector3,
} from "@galacean/engine";
import { SpineAnimationRenderer } from "./SpineAnimationRenderer";
import { AdaptiveTexture } from "./loader/LoaderUtils";
import { ReturnablePool } from "./util/ReturnablePool";
import { ClearablePool } from "./util/ClearablePool";
import { setBlendMode } from "./util/BlendMode";
import { ClearablePool } from "./util/ClearablePool";
import { ReturnablePool } from "./util/ReturnablePool";

class SubRenderItem {
subPrimitive: SubPrimitive;
Expand Down Expand Up @@ -80,6 +77,7 @@ export class SpineGenerator {
_vertexCount,
_subPrimitives,
zSpacing,
premultipliedAlpha,
} = renderer;
let {
tempVerts,
Expand Down Expand Up @@ -168,26 +166,31 @@ export class SpineGenerator {
let finalIndices: ArrayLike<number>;
let finalIndicesLength: number;

let skeleton = slot.bone.skeleton;
let skeletonColor = skeleton.color;
let slotColor = slot.color;
let alpha = skeletonColor.a * slotColor.a * attachmentColor.a;
let color = SpineGenerator.tempColor;
let dark = SpineGenerator.tempDark;
color.set(
skeletonColor.r * slotColor.r * attachmentColor.r,
skeletonColor.g * slotColor.g * attachmentColor.g,
skeletonColor.b * slotColor.b * attachmentColor.b,
alpha,
);
const skeleton = slot.bone.skeleton;
const skeletonColor = skeleton.color;
const slotColor = slot.color;
const finalColor = SpineGenerator.tempColor;
const finalAlpha = skeletonColor.a * slotColor.a * attachmentColor.a;

finalColor.r = skeletonColor.r * slotColor.r * attachmentColor.r;
finalColor.g = skeletonColor.g * slotColor.g * attachmentColor.g;
finalColor.b = skeletonColor.b * slotColor.b * attachmentColor.b;
finalColor.a = finalAlpha;

if (premultipliedAlpha) {
finalColor.r *= finalAlpha;
finalColor.g *= finalAlpha;
finalColor.b *= finalAlpha;
}
GuoLei1990 marked this conversation as resolved.
Show resolved Hide resolved

if (isClipping) {
const dark = SpineGenerator.tempDark;
_clipper.clipTriangles(
tempVerts,
triangles,
triangles.length,
uvs,
color,
finalColor,
dark,
false,
);
Expand All @@ -197,7 +200,7 @@ export class SpineGenerator {
finalIndicesLength = finalIndices.length;
} else {
let verts = tempVerts;
const { r, g, b, a } = color;
const { r, g, b, a } = finalColor;
for (
let v = 2, u = 0, n = numFloats;
v < n;
Expand Down Expand Up @@ -345,7 +348,7 @@ export class SpineGenerator {
const key = `${subTexture.instanceId}_${blendMode}`;
let material = SpineAnimationRenderer._materialCache.get(key);
if (!material) {
material = this._createMaterialForTexture(subTexture, engine, blendMode);
material = this._createMaterialForTexture(subTexture, engine, blendMode, premultipliedAlpha);
SpineAnimationRenderer._materialCache.set(key, material);
}
renderer.setMaterial(i, material);
Expand All @@ -369,10 +372,10 @@ export class SpineGenerator {
this._separateSlotTextureMap.set(slotName, texture);
}

private _createMaterialForTexture(texture: Texture2D, engine: Engine, blendMode: BlendMode): Material {
private _createMaterialForTexture(texture: Texture2D, engine: Engine, blendMode: BlendMode, premultipliedAlpha: boolean): Material {
const material = SpineAnimationRenderer._getDefaultMaterial(engine);
material.shaderData.setTexture("material_SpineTexture", texture);
setBlendMode(material, blendMode);
setBlendMode(material, blendMode, premultipliedAlpha);
return material;
}

Expand Down
6 changes: 3 additions & 3 deletions src/util/BlendMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { BlendFactor, BlendOperation, Material } from "@galacean/engine";
const { SourceAlpha, One, DestinationColor, Zero, OneMinusSourceColor, OneMinusSourceAlpha } = BlendFactor;
const { Add } = BlendOperation;

export function setBlendMode(material: Material, blendMode: BlendMode) {
export function setBlendMode(material: Material, blendMode: BlendMode, premultipliedAlpha: boolean) {
const target = material.renderState.blendState.targetBlendState;
switch (blendMode) {
case BlendMode.Additive:
target.sourceColorBlendFactor = SourceAlpha;
target.sourceColorBlendFactor = premultipliedAlpha ? One : SourceAlpha;
target.destinationColorBlendFactor = One;
target.sourceAlphaBlendFactor = One;
target.destinationAlphaBlendFactor = One;
Expand All @@ -30,7 +30,7 @@ export function setBlendMode(material: Material, blendMode: BlendMode) {
target.colorBlendOperation = target.alphaBlendOperation = Add;
break;
default: // Normal blend default
target.sourceColorBlendFactor = SourceAlpha;
target.sourceColorBlendFactor = premultipliedAlpha ? One : SourceAlpha;
target.destinationColorBlendFactor = OneMinusSourceAlpha;
target.sourceAlphaBlendFactor = One;
target.destinationAlphaBlendFactor = OneMinusSourceAlpha;
Expand Down