How to properly generate textures and png's from SceneCapture2D's (No authoritative answer exists in any AnswerHub post)

(Issues regarding the brightness, contrast and gamma attributes of SceneCapture2D’s texture)

Hello, the following is an issue that has stumped me for about a week.

Summary: I am attempting to create cameras in game that use SceneCapture2Ds to render to a texture whose pixels can be dumped and saved to a PNG or JPEG. The pixel buffers either end up too dark or too gray depending on use of sRGB OR artificial gamma correction I wrote based on [this equation][1] Not only this but whether the images come out “correct” or not is system dependent in that it works fine on three of my coworkers machines and has the exact same issues as mine on one coworkers system ( 3 vs 2). Both dynamic material instance and render target 2d’s are created dynamically and the texture parmeter on the material is set during construction.

  • Simply put exported textures DO NOT
    display as they show in game (Either
    on render targets or in camera view)
    and doing typical gamma correction
    results in muddy textures.Even
    extracting a texture from a correctly
    rendered rendertarget produces bad
    textures with or without correction.

Question: Is there a way to do this correctly regardless of level and hardware? If not what is the easiest most generalizable way to do it semi-specifically?


There is a link at the end of all of this to a zip file that contains much more egregiously discolored photos. Please look at those before rendering any sort of judgement.


Related posts read and whose partial solutions were attempted:

Elabortation:

[WORLD OUTLINE]

[BLUEPRINT]

[CAPTURE COMPONENT SETTINGS]

[MATERIAL BASE SETTINGS]

[RENDERTARGET]

http://s17.postimg.org/jqvuhi8in/Render_Target_Parent.png

[IMAGE METHODS]

    ////////////////////////////////////////////////////////////////////////
    //		CAMERA
    UTexture2D* UREDACTEDFunctionLibrary::TakePhotoWithRenderTarget2D(UTextureRenderTarget2D* RenderTarget, ESavedImageFormat Format, bool& bIsValid, FString& FileLocation)
    {
    	UTexture2D* Result = nullptr;
    	// Save Image from Render Target
    	FRenderTarget* TargetResource = RenderTarget->GameThread_GetRenderTargetResource();
    
    	if (TargetResource == nullptr)
    	{
    		return Result;
    	}
    	TArray<FColor> RawPixels;
    
    
    	bool bCopyValid = ReadTargetToBufferValidated(RawPixels, TargetResource);
    
    
    	if (bCopyValid == false)
    	{
    		return Result;
    	}
    
    	const int32 Width = RenderTarget->SizeX;
    	const int32 Height = RenderTarget->SizeY;
    	// Save Texture to jpg
    	SavePixelBufferToFile(RawPixels, Format, FileLocation, Width, Height);
    	
    	Result = TextureFromPixelBuffer(RawPixels, Width, Height);
    	return Result;
    }
    
    
    void UREDACTEDFunctionLibrary::SavePixelBufferToFile(TArray<FColor> RawPixels, ESavedImageFormat Format, FString& FileLocation, int32 pWidth, int32 pHeight)
    {
    	// Save Texture to jpg
    	FString GameDir = FPaths::ConvertRelativePathToFull(FPaths::GameDir());
    	FString PicsDir = FPaths::Combine(*GameDir, TEXT("PhonePics"));
    	FString Filename = TEXT("DC-") + FilesafeTimestamp() + TEXT("-") + FGuid::NewGuid().ToString();
    	FileLocation = FPaths::Combine(*PicsDir, *Filename);
    	IImageWrapperPtr ImageWrapper = NULL;
    	IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
    	switch (Format)
    	{
    	case ESavedImageFormat::JPG:
    		ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::JPEG);
    		FileLocation += ".jpg";
    		break;
    	case ESavedImageFormat::PNG:
    		ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);
    		FileLocation += ".png";
    		break;
    	case ESavedImageFormat::BMP:
    		ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::BMP);
    		FileLocation += ".bmp";
    		break;
    	case ESavedImageFormat::ICO:
    		ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::ICO);
    		FileLocation += ".ico";
    		break;
    	case ESavedImageFormat::EXR:
    		ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::EXR);
    		FileLocation += ".exr";
    		break;
    	case ESavedImageFormat::ICNS:
    		ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::ICNS);
    		FileLocation += ".icns";
    		break;
    	}
    
    	if (ImageWrapper.IsValid())
    	{
    		if (ImageWrapper->SetRaw(RawPixels.GetData() , RawPixels.Num() * sizeof(FColor), pWidth, pHeight, ERGBFormat::RGBA, 8))
    		{
    			FFileHelper::SaveArrayToFile(ImageWrapper->GetCompressed(), *FileLocation);
    		}
    	}
    }
    
    
    bool UREDACTEDFunctionLibrary::ReadTargetToBufferValidated(TArray<FColor>& pPixelBuffer, FRenderTarget* pTargetResource)
    {
    	if (!pTargetResource->ReadPixels(pPixelBuffer))
    	{
    		return false;
    	}
    	else
    	{
    		for (FColor& Pixel : (pPixelBuffer))
    		{
    			// ^Confirmed - S.
    			const uint8 R = Pixel.B;
    			const uint8 B = Pixel.R;
    			Pixel.R = R;
    			Pixel.B = B;
    			Pixel.A = 255;
    			//Pixel.AlignmentDummy = 0x00FF00;
    
    			// THE FOLLOWING IS GAMMA DECODE: USE IF SRGB IS ON AND ITS NOT A POSTPROCESS MATERIAL
    			/********************
    			float rFloat = ((float)R) / 255.0f;
    			float bFloat = ((float)B) / 255.0f;
    			float gFloat = ((float)Pixel.G) / 255.0f;
    
    			FLinearColor lColor = FLinearColor(rFloat, gFloat, bFloat);
    			Pixel = lColor.ToFColor(true);
    			FColor candColor = FColor();
    			candColor.R = 255 * (rFloat / (rFloat + 0.187f) * 1.035f);
    			candColor.B = 255 * (bFloat / (bFloat + 0.187f) * 1.035f);
    			candColor.G = 255 * (gFloat / (gFloat + 0.187f) * 1.035f);
    			Pixel.R = candColor.R;
    			Pixel.B = candColor.B;
    			Pixel.G = candColor.G;
    			*********************/
    		}
    
    		return true;
    	}
    }
    
    UTexture2D * UREDACTEDFunctionLibrary::Texture2DFromRenderTarget2D(UTextureRenderTarget2D * RenderTarget)
    {
    	UTexture2D* Result = nullptr;
    	// Save Image from Render Target
    	FRenderTarget* TargetResource = RenderTarget->GameThread_GetRenderTargetResource();
    
    
    	if (TargetResource != nullptr)
    	{
    		TArray<FColor> RawPixels;
    		bool bCopyValid = ReadTargetToBufferValidated(RawPixels, TargetResource);
    		if (bCopyValid)
    		{
    			const int32 Width = RenderTarget->SizeX;
    			const int32 Height = RenderTarget->SizeY;
    			Result = TextureFromPixelBuffer(RawPixels, Width, Height);
    		}
    	}
    	return Result;
    }
    
    
    UTexture2D* UREDACTEDFunctionLibrary::TextureFromPixelBuffer(TArray<FColor> RawPixels, int32 pWidth, int32 pHeight)
    {
    	UTexture2D* RetPtr = nullptr;
    		RetPtr = UTexture2D::CreateTransient(pWidth, pHeight, PF_R8G8B8A8);
    		void* TextureData = RetPtr->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE);
    		FMemory::Memcpy(TextureData, &RawPixels[0], RawPixels.Num() * sizeof(FColor));
    		RetPtr->PlatformData->Mips[0].BulkData.Unlock();
    		RetPtr->UpdateResource();
    		return RetPtr;
    }

|| Computers

  • Systems where there are issues:
    • Mine

http://s9.postimg.org/7jz9zy7m7/PC_MINE.png

+ B

http://s27.postimg.org/yyut2x7ab/ss_2016_04_04_at_12_53_20.png

  • Systems without issues:
    • Jas

http://s9.postimg.org/o56wflyq7/PC_JASON.png

+ Jac

http://s9.postimg.org/woqadd72n/PC_JACK.png

+ N

http://s9.postimg.org/umpsz493j/PC_NOAH.png

[PICS (these should all be bright and realistic rather than dark and muddy)][13]

Hello Epic and UE4 Community,

Any chance this question is being currently investigated? I’m aware its a pretty complicated/ heavy duty problem but I figured that supplying all relevant resources (C++ and blueprint source as well as the differing hardware makes for whom the process does and doesn’t work) would make it at least slightly easier to address.

Does anyone have any leads?

Started working on my machine in the most recent engine update. We’ll see if this applies across systems.

I had one particular problem with screen captures in 4.10: There were post process effects applied which I was not able to turn off. I tried overriding settings in an unbound volume and also in the capture component but a faint grain and some histogram clipping kept affecting the rendered image.