---恢复内容开始---
FSlateFontTextureRHI
FSlateFontTextureRHI
if (FontAtlasTexture == nullptr || GlyphAtlasData.TextureIndex != FontTextureIndex) { // Font has a new texture for this glyph. Refresh the batch we use and the index we are currently using FontTextureIndex = GlyphAtlasData.TextureIndex; FontAtlasTexture = FontCache.GetSlateTextureResource(FontTextureIndex); check(FontAtlasTexture); FontShaderResource = ResourceManager.GetFontShaderResource(FontTextureIndex, FontAtlasTexture, FontMaterial); check(FontShaderResource); ElementBatch = &FindBatchForElement(InLayer, FShaderParams(), FontShaderResource, ESlateDrawPrimitive::TriangleList, ESlateShader::Font, InDrawEffects, ESlateBatchDrawFlag::None, DrawElement.GetClippingIndex(), DrawElement.GetSceneIndex());
FSlateDataPayload
FSlateFontTextureRHIResource
FontCache.cpp
bool FSlateFontCache::AddNewEntry( const FCharacterRenderData InRenderData, uint8& OutTextureIndex, uint16& OutGlyphX, uint16& OutGlyphY, uint16& OutGlyphWidth, uint16& OutGlyphHeight ) { const uint64 LastFlushRequestFrameDelta = GFrameCounter - FrameCounterLastFlushRequest; …… }
将renderData拷贝到Slot
void FSlateTextureAtlas::CopyDataIntoSlot( const FAtlasedTextureSlot* SlotToCopyTo, const TArray<uint8>& Data ) { // Copy pixel data to the texture uint8* Start = &AtlasData[SlotToCopyTo->Y*AtlasWidth*BytesPerPixel + SlotToCopyTo->X*BytesPerPixel]; // Account for same padding on each sides const uint32 Padding = GetPaddingAmount(); const uint32 AllPadding = Padding*2; // Make sure the actual slot is not zero-area (otherwise padding could corrupt other images in the atlas) check(SlotToCopyTo->Width > AllPadding); check(SlotToCopyTo->Height > AllPadding); // The width of the source texture without padding (actual width) const uint32 SourceWidth = SlotToCopyTo->Width - AllPadding; const uint32 SourceHeight = SlotToCopyTo->Height - AllPadding; …… }
void FSlateElementBatcher::AddElementsInternal(const TArray<FSlateDrawElement>& DrawElements, const FVector2D& ViewportSize) { checkSlow(DrawLayer); for ( int32 DrawElementIndex = 0; DrawElementIndex < DrawElements.Num(); ++DrawElementIndex ) { const FSlateDrawElement& DrawElement = DrawElements[DrawElementIndex]; // Determine what type of element to add switch ( DrawElement.GetElementType() ) { case FSlateDrawElement::ET_Box: ElmementStat_Boxes++; DrawElement.IsPixelSnapped() ? AddBoxElement<ESlateVertexRounding::Enabled>(DrawElement) : AddBoxElement<ESlateVertexRounding::Disabled>(DrawElement); break; case FSlateDrawElement::ET_Border: ElmementStat_Borders++; DrawElement.IsPixelSnapped() ? AddBorderElement<ESlateVertexRounding::Enabled>(DrawElement) : AddBorderElement<ESlateVertexRounding::Disabled>(DrawElement); break; case FSlateDrawElement::ET_Text: ElmementStat_Text++; DrawElement.IsPixelSnapped() ? AddTextElement<ESlateVertexRounding::Enabled>(DrawElement) : AddTextElement<ESlateVertexRounding::Disabled>(DrawElement); break; case FSlateDrawElement::ET_ShapedText: ElmementStat_ShapedText++; DrawElement.IsPixelSnapped() ? AddShapedTextElement<ESlateVertexRounding::Enabled>(DrawElement) : AddShapedTextElement<ESlateVertexRounding::Disabled>(DrawElement); break; case FSlateDrawElement::ET_Line: ElmementStat_Line++; DrawElement.IsPixelSnapped() ? AddLineElement<ESlateVertexRounding::Enabled>(DrawElement) : AddLineElement<ESlateVertexRounding::Disabled>(DrawElement); break; case FSlateDrawElement::ET_DebugQuad: ElmementStat_Other++; DrawElement.IsPixelSnapped() ? AddQuadElement<ESlateVertexRounding::Enabled>(DrawElement) : AddQuadElement<ESlateVertexRounding::Disabled>(DrawElement); break; case FSlateDrawElement::ET_Spline: // Note that we ignore pixel snapping here; see implementation for more info. ElmementStat_Other++; AddSplineElement(DrawElement); break; case FSlateDrawElement::ET_Gradient: ElmementStat_Other++; DrawElement.IsPixelSnapped() ? AddGradientElement<ESlateVertexRounding::Enabled>(DrawElement) : AddGradientElement<ESlateVertexRounding::Disabled>(DrawElement); break; case FSlateDrawElement::ET_Viewport: ElmementStat_Other++; DrawElement.IsPixelSnapped() ? AddViewportElement<ESlateVertexRounding::Enabled>(DrawElement) : AddViewportElement<ESlateVertexRounding::Disabled>(DrawElement); break; case FSlateDrawElement::ET_Custom: ElmementStat_Other++; AddCustomElement(DrawElement); break; case FSlateDrawElement::ET_CustomVerts: ElmementStat_Other++; AddCustomVerts(DrawElement); break; case FSlateDrawElement::ET_Layer: ElmementStat_Other++; AddLayer(DrawElement); break; case FSlateDrawElement::ET_CachedBuffer: ElmementStat_CachedBuffer++; AddCachedBuffer(DrawElement); break; case FSlateDrawElement::ET_PostProcessPass: ElmementStat_Other++; AddPostProcessPass(DrawElement, ViewportSize); break; default: checkf(0, TEXT("Invalid element type")); break; } } }
这个函数根据类型DrawElement类型调用不同的函数,当类型为text时,调用AddTextElement函数;
当类型为ShapedTextElement时,调用AddShapedTextElement,(void FSlateElementBatcher::AddShapedTextElement( const FSlateDrawElement& DrawElement ))它里面的
auto BuildFontGeometry = [&](const FFontOutlineSettings& InOutlineSettings, const FColor& InTint, const UObject* FontMaterial, int32 InLayer, int32 InHorizontalOffset) { FVector2D TopLeft(0, 0); const float PosX = TopLeft.X+InHorizontalOffset; float PosY = TopLeft.Y; float LineX = PosX; float LineY = PosY; int32 FontTextureIndex = -1; FSlateShaderResource* FontAtlasTexture = nullptr; FSlateShaderResource* FontShaderResource = nullptr; FSlateElementBatch* ElementBatch = nullptr; FSlateVertexArray* BatchVertices = nullptr; FSlateIndexArray* BatchIndices = nullptr; uint32 VertexOffset = 0; uint32 IndexOffset = 0; float InvTextureSizeX = 0; float InvTextureSizeY = 0; const bool bIsFontMaterial = FontMaterial != nullptr; // Optimize by culling bool bEnableCulling = false; float LocalClipBoundingBoxLeft = 0; float LocalClipBoundingBoxRight = 0; if (GlyphsToRender.Num() > 200) { int16 ClippingIndex = DrawElement.GetClippingIndex(); if (ClippingStates && ClippingStates->IsValidIndex(ClippingIndex)) { const FSlateClippingState& ClippingState = (*ClippingStates)[ClippingIndex]; if (ClippingState.ScissorRect.IsSet() && ClippingState.ScissorRect->IsAxisAligned() && RenderTransform.GetMatrix().IsIdentity()) { bEnableCulling = true; const FSlateRect LocalClipBoundingBox = TransformRect(RenderTransform.Inverse(), ClippingState.ScissorRect->GetBoundingBox()); LocalClipBoundingBoxLeft = LocalClipBoundingBox.Left; LocalClipBoundingBoxRight = LocalClipBoundingBox.Right; } } } const int32 NumGlyphs = GlyphsToRender.Num(); for (int32 GlyphIndex = 0; GlyphIndex < NumGlyphs; ++GlyphIndex) { const FShapedGlyphEntry& GlyphToRender = GlyphsToRender[GlyphIndex]; if (GlyphToRender.bIsVisible) { const FShapedGlyphFontAtlasData GlyphAtlasData = FontCache.GetShapedGlyphFontAtlasData(GlyphToRender, InOutlineSettings); …… // }
里面调用了FontCache.GetShapedGlyphFontAtlasData(GlyphToRender, InOutlineSettings)
许多地方都可能调到这个地方,比如:
bool FSlateFontCache::AddNewEntry(const FShapedGlyphEntry& InShapedGlyph, const FFontOutlineSettings& InOutlineSettings, FShapedGlyphFontAtlasData& OutAtlasData) { // Render the glyph FCharacterRenderData RenderData; const bool bDidRender = FontRenderer->GetRenderData(InShapedGlyph, InOutlineSettings, RenderData); OutAtlasData.Valid = bDidRender && AddNewEntry(RenderData, OutAtlasData.TextureIndex, OutAtlasData.StartU, OutAtlasData.StartV, OutAtlasData.USize, OutAtlasData.VSize); if (OutAtlasData.Valid) { OutAtlasData.VerticalOffset = RenderData.MeasureInfo.VerticalOffset; OutAtlasData.HorizontalOffset = RenderData.MeasureInfo.HorizontalOffset; } return OutAtlasData.Valid; }
里面的GetRenderData函数
bool FSlateFontRenderer::GetRenderData(const FShapedGlyphEntry& InShapedGlyph, const FFontOutlineSettings& InOutlineSettings, FCharacterRenderData& OutRenderData) const { #if WITH_FREETYPE SCOPE_CYCLE_COUNTER(STAT_FreetypeRenderGlyph); FFreeTypeFaceGlyphData FaceGlyphData; FaceGlyphData.FaceAndMemory = InShapedGlyph.FontFaceData->FontFace.Pin(); FaceGlyphData.GlyphIndex = InShapedGlyph.GlyphIndex; FaceGlyphData.GlyphFlags = InShapedGlyph.FontFaceData->GlyphFlags; if (FaceGlyphData.FaceAndMemory.IsValid()) { check(FaceGlyphData.FaceAndMemory->IsValid()); FT_Error Error = FreeTypeUtils::LoadGlyph(FaceGlyphData.FaceAndMemory->GetFace(), FaceGlyphData.GlyphIndex, FaceGlyphData.GlyphFlags, InShapedGlyph.FontFaceData->FontSize, InShapedGlyph.FontFaceData->FontScale); check(Error == 0); OutRenderData.Char = 0; return GetRenderDataInternal(FaceGlyphData, InShapedGlyph.FontFaceData->FontScale, InOutlineSettings, OutRenderData); } #endif // WITH_FREETYPE return false; }
LoadGlyph获得字体的大小,scale矩阵。GetRenderDataInternal获得要渲染的像素长宽,填充像素。
然后AddNewEntry函数又调用了另一个重载的AddNewEntry函数,这个函数主要作用是:根据上面求出的RenderData的数据,将Character加到AtlasedTextureSlot里。然后将这个图集贴图Slot加到图集里。得到这个字母的x, y, 宽,高值。
bool FSlateFontCache::AddNewEntry( const FCharacterRenderData InRenderData, uint8& OutTextureIndex, uint16& OutGlyphX, uint16& OutGlyphY, uint16& OutGlyphWidth, uint16& OutGlyphHeight ) { const uint64 LastFlushRequestFrameDelta = GFrameCounter - FrameCounterLastFlushRequest; // Will this entry fit within any atlas texture? if (InRenderData.MeasureInfo.SizeX > FontAtlasFactory->GetAtlasSize().X || InRenderData.MeasureInfo.SizeY > FontAtlasFactory->GetAtlasSize().Y) { TSharedPtr<ISlateFontTexture> NonAtlasedTexture = FontAtlasFactory->CreateNonAtlasedTexture(InRenderData.MeasureInfo.SizeX, InRenderData.MeasureInfo.SizeY, InRenderData.RawPixels); if (NonAtlasedTexture.IsValid()) { INC_DWORD_STAT_BY(STAT_SlateNumFontNonAtlasedTextures, 1); UE_LOG(LogSlate, Warning, TEXT("SlateFontCache - Glyph texture is too large to store in the font atlas, so we're falling back to a non-atlased texture for this glyph. This may have SERIOUS performance implications. Atlas page size: { %d, %d }. Glyph render size: { %d, %d }"), FontAtlasFactory->GetAtlasSize().X, FontAtlasFactory->GetAtlasSize().Y, InRenderData.MeasureInfo.SizeX, InRenderData.MeasureInfo.SizeY ); NonAtlasedTextures.Add(NonAtlasedTexture.ToSharedRef()); OutTextureIndex = AllFontTextures.Add(NonAtlasedTexture.ToSharedRef()); OutGlyphX = 0; OutGlyphY = 0; OutGlyphWidth = InRenderData.MeasureInfo.SizeX; OutGlyphHeight = InRenderData.MeasureInfo.SizeY; if (NonAtlasedTextures.Num() > CurrentMaxNonAtlasedTexturesBeforeFlushRequest && !bFlushRequested) { // The InitialMaxNonAtlasedTexturesBeforeFlushRequest may have changed since last flush, // so double check that we're below the current max or initial, if we're under it, // update the current to the initial. if (NonAtlasedTextures.Num() <= InitialMaxNonAtlasedTexturesBeforeFlushRequest) { CurrentMaxNonAtlasedTexturesBeforeFlushRequest = InitialMaxNonAtlasedTexturesBeforeFlushRequest; } // If we grew back up to this number of non-atlased textures within the same or next frame of the previous flush request, then we likely legitimately have // a lot of font data cached. We should update CurrentMaxNonAtlasedTexturesBeforeFlushRequest to give us a bit more flexibility before the next flush request else if (LastFlushRequestFrameDelta <= GrowFontNonAtlasFrameWindow) { CurrentMaxNonAtlasedTexturesBeforeFlushRequest = NonAtlasedTextures.Num(); UE_LOG(LogSlate, Warning, TEXT("SlateFontCache - Setting the threshold to trigger a flush to %d non-atlased textures as there is a lot of font data being cached."), CurrentMaxNonAtlasedTexturesBeforeFlushRequest); } else { // We've grown beyond our current stable limit - try and request a flush RequestFlushCache(FString::Printf(TEXT("Large glyph font atlases out of space; %d/%d Textures; frames since last flush: %llu"), NonAtlasedTextures.Num(), CurrentMaxNonAtlasedTexturesBeforeFlushRequest, LastFlushRequestFrameDelta)); } } return true; } UE_LOG(LogSlate, Warning, TEXT("SlateFontCache - Glyph texture is too large to store in the font atlas, but we cannot support rendering such a large texture. Atlas page size: { %d, %d }. Glyph render size: { %d, %d }"), FontAtlasFactory->GetAtlasSize().X, FontAtlasFactory->GetAtlasSize().Y, InRenderData.MeasureInfo.SizeX, InRenderData.MeasureInfo.SizeY ); return false; } auto FillOutputParamsFromAtlasedTextureSlot = [&](const FAtlasedTextureSlot& AtlasedTextureSlot) { OutGlyphX = AtlasedTextureSlot.X + AtlasedTextureSlot.Padding; OutGlyphY = AtlasedTextureSlot.Y + AtlasedTextureSlot.Padding; OutGlyphWidth = AtlasedTextureSlot.Width - (2 * AtlasedTextureSlot.Padding); OutGlyphHeight = AtlasedTextureSlot.Height - (2 * AtlasedTextureSlot.Padding); }; for( OutTextureIndex = 0; OutTextureIndex < FontAtlases.Num(); ++OutTextureIndex ) { // Add the character to the texture const FAtlasedTextureSlot* NewSlot = FontAtlases[OutTextureIndex]->AddCharacter(InRenderData); if( NewSlot ) { FillOutputParamsFromAtlasedTextureSlot(*NewSlot); return true; } } TSharedRef<FSlateFontAtlas> FontAtlas = FontAtlasFactory->CreateFontAtlas(); // Add the character to the texture const FAtlasedTextureSlot* NewSlot = FontAtlas->AddCharacter(InRenderData); if( NewSlot ) { FillOutputParamsFromAtlasedTextureSlot(*NewSlot); } FontAtlases.Add( FontAtlas ); OutTextureIndex = AllFontTextures.Add(FontAtlas); INC_DWORD_STAT_BY( STAT_SlateNumFontAtlases, 1 ); if( FontAtlases.Num() > CurrentMaxAtlasPagesBeforeFlushRequest && !bFlushRequested ) { // The InitialMaxNonAtlasedTexturesBeforeFlushRequest may have changed since last flush, // so double check that we're below the current max or initial, if we're under it, // update the current to the initial. if (FontAtlases.Num() <= InitialMaxAtlasPagesBeforeFlushRequest) { CurrentMaxAtlasPagesBeforeFlushRequest = InitialMaxAtlasPagesBeforeFlushRequest; } // If we grew back up to this number of atlas pages within the same or next frame of the previous flush request, then we likely legitimately have // a lot of font data cached. We should update MaxAtlasPagesBeforeFlushRequest to give us a bit more flexibility before the next flush request else if (LastFlushRequestFrameDelta <= GrowFontAtlasFrameWindow) { CurrentMaxAtlasPagesBeforeFlushRequest = FontAtlases.Num(); UE_LOG(LogSlate, Warning, TEXT("SlateFontCache - Setting the threshold to trigger a flush to %d atlas pages as there is a lot of font data being cached."), CurrentMaxAtlasPagesBeforeFlushRequest); } else { // We've grown beyond our current stable limit - try and request a flush RequestFlushCache(FString::Printf(TEXT("Font Atlases Full; %d/%d Pages; frames since last flush: %llu"), FontAtlases.Num(), CurrentMaxAtlasPagesBeforeFlushRequest, LastFlushRequestFrameDelta)); } } return NewSlot != nullptr; }
通过这个AddMewEntry,就把这个字母相关的渲染数据写入到图集贴图里了。
后面在ElementBatcher.cpp文件中,AddShapedTextElement函数中,BuildFontGeometry函数就会用到图集信息
if (bOutlineFont) { // Build geometry for the outline BuildFontGeometry(OutlineSettings, PackVertexColor(DrawElementPayload.GetOutlineTint()), OutlineFontMaterial, Layer, 0); //The fill area was measured without an outline so it must be shifted by the scaled outline size float HorizontalOffset = FMath::RoundToFloat(OutlineSize * FontScale); // Build geometry for the base font which is always rendered on top of the outline BuildFontGeometry(FFontOutlineSettings::NoOutline, BaseTint, BaseFontMaterial, Layer+1, HorizontalOffset); } else { // No outline BuildFontGeometry(FFontOutlineSettings::NoOutline, BaseTint, BaseFontMaterial, Layer, 0); }
shapedText的 AddShapedTextElement里的BuildFontGeometry函数:
auto BuildFontGeometry = [&](const FFontOutlineSettings& InOutlineSettings, const FColor& InTint, const UObject* FontMaterial, int32 InLayer, int32 InHorizontalOffset) { FVector2D TopLeft(0, 0); const float PosX = TopLeft.X+InHorizontalOffset; float PosY = TopLeft.Y; float LineX = PosX; float LineY = PosY; int32 FontTextureIndex = -1; FSlateShaderResource* FontAtlasTexture = nullptr; FSlateShaderResource* FontShaderResource = nullptr; FSlateElementBatch* ElementBatch = nullptr; FSlateVertexArray* BatchVertices = nullptr; FSlateIndexArray* BatchIndices = nullptr; uint32 VertexOffset = 0; uint32 IndexOffset = 0; float InvTextureSizeX = 0; float InvTextureSizeY = 0; const bool bIsFontMaterial = FontMaterial != nullptr; // Optimize by culling bool bEnableCulling = false; float LocalClipBoundingBoxLeft = 0; float LocalClipBoundingBoxRight = 0; if (GlyphsToRender.Num() > 200) { int16 ClippingIndex = DrawElement.GetClippingIndex(); if (ClippingStates && ClippingStates->IsValidIndex(ClippingIndex)) { const FSlateClippingState& ClippingState = (*ClippingStates)[ClippingIndex]; if (ClippingState.ScissorRect.IsSet() && ClippingState.ScissorRect->IsAxisAligned() && RenderTransform.GetMatrix().IsIdentity()) { bEnableCulling = true; const FSlateRect LocalClipBoundingBox = TransformRect(RenderTransform.Inverse(), ClippingState.ScissorRect->GetBoundingBox()); LocalClipBoundingBoxLeft = LocalClipBoundingBox.Left; LocalClipBoundingBoxRight = LocalClipBoundingBox.Right; } } } const int32 NumGlyphs = GlyphsToRender.Num(); for (int32 GlyphIndex = 0; GlyphIndex < NumGlyphs; ++GlyphIndex) { const FShapedGlyphEntry& GlyphToRender = GlyphsToRender[GlyphIndex]; if (GlyphToRender.bIsVisible) { const FShapedGlyphFontAtlasData GlyphAtlasData = FontCache.GetShapedGlyphFontAtlasData(GlyphToRender, InOutlineSettings); if (GlyphAtlasData.Valid) { const float X = LineX + GlyphAtlasData.HorizontalOffset + GlyphToRender.XOffset; // Note PosX,PosY is the upper left corner of the bounding box representing the string. This computes the Y position of the baseline where text will sit if (bEnableCulling) { if (X + GlyphAtlasData.USize < LocalClipBoundingBoxLeft) { LineX += GlyphToRender.XAdvance; LineY += GlyphToRender.YAdvance; continue; } else if (X > LocalClipBoundingBoxRight) { break; } } if (FontAtlasTexture == nullptr || GlyphAtlasData.TextureIndex != FontTextureIndex) { // Font has a new texture for this glyph. Refresh the batch we use and the index we are currently using FontTextureIndex = GlyphAtlasData.TextureIndex; FontAtlasTexture = FontCache.GetSlateTextureResource(FontTextureIndex); check(FontAtlasTexture); FontShaderResource = ResourceManager.GetFontShaderResource(FontTextureIndex, FontAtlasTexture, FontMaterial); check(FontShaderResource); ElementBatch = &FindBatchForElement(InLayer, FShaderParams(), FontShaderResource, ESlateDrawPrimitive::TriangleList, ESlateShader::Font, InDrawEffects, ESlateBatchDrawFlag::None, DrawElement.GetClippingIndex(), DrawElement.GetSceneIndex()); BatchVertices = &BatchData->GetBatchVertexList(*ElementBatch); BatchIndices = &BatchData->GetBatchIndexList(*ElementBatch); VertexOffset = BatchVertices->Num(); IndexOffset = BatchIndices->Num(); InvTextureSizeX = 1.0f / FontAtlasTexture->GetWidth(); InvTextureSizeY = 1.0f / FontAtlasTexture->GetHeight(); } const float Y = LineY - GlyphAtlasData.VerticalOffset + GlyphToRender.YOffset + MaxHeight + TextBaseline; const float U = GlyphAtlasData.StartU * InvTextureSizeX; const float V = GlyphAtlasData.StartV * InvTextureSizeY; const float SizeX = GlyphAtlasData.USize; const float SizeY = GlyphAtlasData.VSize; const float SizeU = GlyphAtlasData.USize * InvTextureSizeX; const float SizeV = GlyphAtlasData.VSize * InvTextureSizeY; { FSlateVertexArray& BatchVerticesRef = *BatchVertices; FSlateIndexArray& BatchIndicesRef = *BatchIndices; const FVector2D UpperLeft(X, Y); const FVector2D UpperRight(X + SizeX, Y); const FVector2D LowerLeft(X, Y + SizeY); const FVector2D LowerRight(X + SizeX, Y + SizeY); // Add four vertices for this quad BatchVerticesRef.AddUninitialized(4); // Add six indices for this quad BatchIndicesRef.AddUninitialized(6); // The start index of these vertices in the index buffer uint32 IndexStart = VertexOffset; float Ut = 0.0f, Vt = 0.0f, UtMax = 0.0f, VtMax = 0.0f; if (bIsFontMaterial) { float DistAlpha = (float)GlyphIndex / NumGlyphs; float DistAlphaNext = (float)(GlyphIndex + 1) / NumGlyphs; // This creates a set of UV's that goes from 0-1, from left to right of the string in U and 0-1 baseline to baseline top to bottom in V Ut = FMath::Lerp(0.0f, 1.0f, DistAlpha); Vt = FMath::Lerp(0.0f, 1.0f, UpperLeft.Y / MaxHeight); UtMax = FMath::Lerp(0.0f, 1.0f, DistAlphaNext); VtMax = FMath::Lerp(0.0f, 1.0f, LowerLeft.Y / MaxHeight); } // Add four vertices to the list of verts to be added to the vertex buffer BatchVerticesRef[VertexOffset++] = FSlateVertex::Make<Rounding>(RenderTransform, UpperLeft, FVector4(U, V, Ut, Vt), FVector2D(0.0f, 0.0f), InTint ); BatchVerticesRef[VertexOffset++] = FSlateVertex::Make<Rounding>(RenderTransform, FVector2D(LowerRight.X, UpperLeft.Y), FVector4(U + SizeU, V, UtMax, Vt), FVector2D(1.0f, 0.0f), InTint ); BatchVerticesRef[VertexOffset++] = FSlateVertex::Make<Rounding>(RenderTransform, FVector2D(UpperLeft.X, LowerRight.Y), FVector4(U, V + SizeV, Ut, VtMax), FVector2D(0.0f, 1.0f), InTint ); BatchVerticesRef[VertexOffset++] = FSlateVertex::Make<Rounding>(RenderTransform, LowerRight, FVector4(U + SizeU, V + SizeV, UtMax, VtMax), FVector2D(1.0f, 1.0f), InTint ); BatchIndicesRef[IndexOffset++] = IndexStart + 0; BatchIndicesRef[IndexOffset++] = IndexStart + 1; BatchIndicesRef[IndexOffset++] = IndexStart + 2; BatchIndicesRef[IndexOffset++] = IndexStart + 1; BatchIndicesRef[IndexOffset++] = IndexStart + 3; BatchIndicesRef[IndexOffset++] = IndexStart + 2; } } } LineX += GlyphToRender.XAdvance; LineY += GlyphToRender.YAdvance; } };
普通text的AddTextElement,跟ShapedText不同。 根据characterList得到characterEntry,这个entry上有textureindex,从而获得图集贴图,联合材质球构成shaderResource。然后获得顶点和索引,uv。
两者跟outline相关的地方:
FCharacterList& CharacterList = FontCache.GetCharacterList(DrawElementPayload.GetFontInfo(), FontScale, InOutlineSettings);
const FShapedGlyphFontAtlasData GlyphAtlasData = FontCache.GetShapedGlyphFontAtlasData(GlyphToRender, InOutlineSettings);
先看第二个,返回的是FShapedGlyphFontAtlasData数据,从AddNewEntry(InShapedGlyph, InOutlineSettings, *NewAtlasData);获取到的。上面说过了。
然后看看顶点属性
template<ESlateVertexRounding Rounding> static FSlateVertex Make(const FSlateRenderTransform& RenderTransform, const FVector2D& InLocalPosition, const FVector4& InTexCoords, const FVector2D& InMaterialTexCoords, const FColor& InColor) { FSlateVertex Vertex; Vertex.TexCoords[0] = InTexCoords.X; Vertex.TexCoords[1] = InTexCoords.Y; Vertex.TexCoords[2] = InTexCoords.Z; Vertex.TexCoords[3] = InTexCoords.W; Vertex.MaterialTexCoords = InMaterialTexCoords; Vertex.InitCommon<Rounding>(RenderTransform, InLocalPosition, InColor); return Vertex; }
因为看到祖龙的龙族幻想里面,同样坐标的顶点出现了两次,只有InAttribute3这个属性不一样。如下
VTX IDX in_ATTRIBUTE0.x in_ATTRIBUTE0.y in_ATTRIBUTE0.z in_ATTRIBUTE0.w in_ATTRIBUTE1.x in_ATTRIBUTE1.y in_ATTRIBUTE2.x in_ATTRIBUTE2.y in_ATTRIBUTE2.z in_ATTRIBUTE3.x in_ATTRIBUTE3.y in_ATTRIBUTE3.z in_ATTRIBUTE3.w 2 2 0.16113 0.06152 0 0 0 1 1962.7439 208.84578 0.99947 0 0 0 0.4 104 70 0.25684 0.06152 0 0 0 1 1962.7439 208.84578 0.99947 0.83922 0.94118 0.60392 1
输出的,颜色和var_texcoord1不一样。
half3 sRGBToLinear( half3 Color )
{
Color = max(6.10352e-5, Color); // minimum positive non-denormal (fixes black problem on DX11 AMD and NV)
return Color > 0.04045 ? pow( Color * (1.0 / 1.055) + 0.0521327, 2.4 ) : Color * (1.0 / 12.92);
}
half3 sRGBToLinear( half3 Color )
{
Color = max(6.10352e-5, Color); // minimum positive non-denormal (fixes black problem on DX11 AMD and NV)
return pow( Color * (1.0 / 1.055) + 0.0521327, 2.4 );
}
FindBatchForElement这个函数会查找,是否有能跟当前text合批的text,首先查找这个layer,找到了,则将这个element加到batch。如果没有,则创建一个新的batch:
FSlateElementBatch& FSlateElementBatcher::FindBatchForElement( uint32 Layer, const FShaderParams& ShaderParams, const FSlateShaderResource* InTexture, ESlateDrawPrimitive::Type PrimitiveType, ESlateShader::Type ShaderType, ESlateDrawEffect DrawEffects, ESlateBatchDrawFlag DrawFlags, int32 ClippingIndex, int32 SceneIndex) { SCOPE_CYCLE_COUNTER( STAT_SlateFindBatchForElement ); FElementBatchMap& LayerToElementBatches = DrawLayer->GetElementBatchMap(); // See if the layer already exists. TUniqueObj<FElementBatchArray>* ElementBatches = LayerToElementBatches.Find( Layer ); if( !ElementBatches ) { // The layer doesn't exist so make it now ElementBatches = &LayerToElementBatches.Add( Layer ); } checkSlow( ElementBatches ); // Create a temp batch so we can use it as our key to find if the same batch already exists FSlateElementBatch TempBatch( InTexture, ShaderParams, ShaderType, PrimitiveType, DrawEffects, DrawFlags, ClippingIndex, *ClippingStates, 0, 0, nullptr, SceneIndex ); FSlateElementBatch* ElementBatch = (*ElementBatches)->FindByKey( TempBatch ); if( !ElementBatch ) { // No batch with the specified parameter exists. Create it from the temp batch. int32 Index = (*ElementBatches)->Add( TempBatch ); ElementBatch = &(**ElementBatches)[Index]; BatchData->AssignVertexArrayToBatch(*ElementBatch); BatchData->AssignIndexArrayToBatch(*ElementBatch); } check( ElementBatch ); // Increment the number of elements in the batch. ++ElementBatch->NumElementsInBatch; return *ElementBatch; }