• 字体渲染过程及合批研究


    ---恢复内容开始---

    ElementBatcher.cpp文件
    AddTextElement函数里调用了
    BuildFontGeometry函数,如下
    ·
    345678
    通过fontInfo得到CharacterList,

    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;
    }
    

      

  • 相关阅读:
    e666. 创建缓冲图像
    e670. 缓冲图像转换为图像
    e680. 使三元色图像变明变暗
    e659. 缩放,剪取,移动,翻转图形
    e656. 创建基本图形
    e657. 用直线和曲线绘制图形
    e658. 组合图形
    e655. 混合风格的文本
    e654. 获得文本的缩略图
    e652. Getting the Font Faces for a Font Family
  • 原文地址:https://www.cnblogs.com/Shaojunping/p/11543391.html
Copyright © 2020-2023  润新知