Create an HD Video Player with HDR Tech - Huawei Developers

{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
​What Is HDR and Why Does It Matter​Streaming technology has improved significantly, giving rise to higher and higher video resolutions from those at or below 480p (which are known as standard definition or SD for short) to those at or above 720p (high definition, or HD for short).
The video resolution is vital for all apps. A research that I recently came across backs this up: 62% of people are more likely to negatively perceive a brand that provides a poor-quality video experience, while 57% of people are less likely to share a poor-quality video. With this in mind, it's no wonder that there are so many emerging solutions to enhance video resolution.
One solution is HDR — high dynamic range. It is a post-processing method used in imaging and photography, which mimics what a human eye can see by giving more details to dark areas and improving the contrast. When used in a video player, HDR can deliver richer videos with a higher resolution.
Many HDR solutions, however, are let down by annoying restrictions. These can include a lack of unified technical specifications, high level of difficulty for implementing them, and a requirement for videos in ultra-high definition. I tried to look for a solution without such restrictions and luckily, I found one. That's the HDR Vivid SDK from HMS Core Video Kit. This solution is packed with image-processing features like the opto-electronic transfer function (OETF), tone mapping, and HDR2SDR. With these features, the SDK can equip a video player with richer colors, higher level of detail, and more.
I used the SDK together with the HDR Ability SDK (which can also be used independently) to try the latter's brightness adjustment feature, and found that they could deliver an even better HDR video playback experience. And on that note, I'd like to share how I used these two SDKs to create a video player.
Before Development​1. Configure the app information as needed in AppGallery Connect.
2. Integrate the HMS Core SDK.
For Android Studio, the SDK can be integrated via the Maven repository. Before the development procedure, the SDK needs to be integrated into the Android Studio project.
3. Configure the obfuscation scripts.
4. Add permissions, including those for accessing the Internet, for obtaining the network status, for accessing the Wi-Fi network, for writing data into the external storage, for reading data from the external storage, for reading device information, for checking whether a device is rooted, and obtaining the wake lock. (The last three permissions are optional.)
App Development​Preparations​1. Check whether the device is capable of decoding an HDR Vivid video. If the device has such a capability, the following function will return true.
Code:
public boolean isSupportDecode() {
// Check whether the device supports MediaCodec.
MediaCodecList mcList = new MediaCodecList(MediaCodecList.ALL_CODECS);
MediaCodecInfo[] mcInfos = mcList.getCodecInfos();
for (MediaCodecInfo mci : mcInfos) {
// Filter out the encoder.
if (mci.isEncoder()) {
continue;
}
String[] types = mci.getSupportedTypes();
String typesArr = Arrays.toString(types);
// Filter out the non-HEVC decoder.
if (!typesArr.contains("hevc")) {
continue;
}
for (String type : types) {
// Check whether 10-bit HEVC decoding is supported.
MediaCodecInfo.CodecCapabilities codecCapabilities = mci.getCapabilitiesForType(type);
for (MediaCodecInfo.CodecProfileLevel codecProfileLevel : codecCapabilities.profileLevels) {
if (codecProfileLevel.profile == HEVCProfileMain10
|| codecProfileLevel.profile == HEVCProfileMain10HDR10
|| codecProfileLevel.profile == HEVCProfileMain10HDR10Plus) {
// true means supported.
return true;
}
}
}
}
// false means unsupported.
return false;
}
2. Parse a video to obtain information about its resolution, OETF, color space, and color format. Then save the information in a custom variable. In the example below, the variable is named as VideoInfo.
Code:
public class VideoInfo {
private int width;
private int height;
private int tf;
private int colorSpace;
private int colorFormat;
private long durationUs;
}
3. Create a SurfaceView object that will be used by the SDK to process the rendered images.
Code:
// surface_view is defined in a layout file.
SurfaceView surfaceView = (SurfaceView) view.findViewById(R.id.surface_view);
4. Create a thread to parse video streams from a video.
Rendering and Transcoding a Video​1. Create and then initialize an instance of HdrVividRender.
Code:
HdrVividRender hdrVividRender = new HdrVividRender();
hdrVividRender.init();
2. Configure the OETF and resolution for the video source.
Code:
// Configure the OETF.
hdrVividRender.setTransFunc(2);
// Configure the resolution.
hdrVividRender.setInputVideoSize(3840, 2160);
When the SDK is used on an Android device, only the rendering mode for input is supported.
3. Configure the brightness for the output. This step is optional.
Code:
hdrVividRender.setBrightness(700);
4. Create a Surface object, which will serve as the input. This method is called when HdrVividRender works in rendering mode, and the created Surface object is passed as the inputSurface parameter of configure to the SDK.
Code:
Surface inputSurface = hdrVividRender.createInputSurface();
5. Configure the output parameters.
Set the dimensions of the rendered Surface object. This step is necessary in the rendering mode for output.
Code:
// surfaceView is the video playback window.
hdrVividRender.setOutputSurfaceSize(surfaceView.getWidth(), surfaceView.getHeight());
Set the color space for the buffered output video, which can be set in the transcoding mode for output. This step is optional. However, when no color space is set, BT.709 is used by default.
Code:
hdrVividRender.setColorSpace(HdrVividRender.COLORSPACE_P3);
Set the color format for the buffered output video, which can be set in the transcoding mode for output. This step is optional. However, when no color format is specified, R8G8B8A8 is used by default.
Code:
hdrVividRender.setColorFormat(HdrVividRender.COLORFORMAT_R8G8B8A8);
6. When the rendering mode is used as the output mode, the following APIs are required.
Code:
hdrVividRender.configure(inputSurface, new HdrVividRender.InputCallback() {
@Override
public int onGetDynamicMetaData(HdrVividRender hdrVividRender, long pts) {
// Set the static metadata, which needs to be obtained from the video source.
HdrVividRender.StaticMetaData lastStaticMetaData = new HdrVividRender.StaticMetaData();
hdrVividRender.setStaticMetaData(lastStaticMetaData);
// Set the dynamic metadata, which also needs to be obtained from the video source.
ByteBuffer dynamicMetaData = ByteBuffer.allocateDirect(10);
hdrVividRender.setDynamicMetaData(20000, dynamicMetaData);
return 0;
}
}, surfaceView.getHolder().getSurface(), null);
7. When the transcoding mode is used as the output mode, call the following APIs.
Code:
hdrVividRender.configure(inputSurface, new HdrVividRender.InputCallback() {
@Override
public int onGetDynamicMetaData(HdrVividRender hdrVividRender, long pts) {
// Set the static metadata, which needs to be obtained from the video source.
HdrVividRender.StaticMetaData lastStaticMetaData = new HdrVividRender.StaticMetaData();
hdrVividRender.setStaticMetaData(lastStaticMetaData);
// Set the dynamic metadata, which also needs to be obtained from the video source.
ByteBuffer dynamicMetaData = ByteBuffer.allocateDirect(10);
hdrVividRender.setDynamicMetaData(20000, dynamicMetaData);
return 0;
}
}, null, new HdrVividRender.OutputCallback() {
@Override
public void onOutputBufferAvailable(HdrVividRender hdrVividRender, ByteBuffer byteBuffer,
HdrVividRender.BufferInfo bufferInfo) {
// Process the buffered data.
}
});
new HdrVividRender.OutputCallback() is used for asynchronously processing the returned buffered data. If this method is not used, the read method can be used instead. For example:
Code:
hdrVividRender.read(new BufferInfo(), 10); // 10 is a timestamp, which is determined by your app.
8. Start the processing flow.
Code:
hdrVividRender.start();
9. Stop the processing flow.
Code:
hdrVividRender.stop();
10. Release the resources that have been occupied.
Code:
hdrVividRender.release();
hdrVividRender = null;
During the above steps, I noticed that when the dimensions of Surface change, setOutputSurfaceSize has to be called to re-configure the dimensions of the Surface output.
Besides, in the rendering mode for output, when WisePlayer is switched from the background to the foreground or vice versa, the Surface object will be destroyed and then re-created. In this case, there is a possibility that the HdrVividRender instance is not destroyed. If so, the setOutputSurface API needs to be called so that a new Surface output can be set.
Setting Up HDR Capabilities​HDR capabilities are provided in the class HdrAbility. It can be used to adjust brightness when the HDR Vivid SDK is rendering or transcoding an HDR Vivid video.
1. Initialize the function of brightness adjustment.
Code:
HdrAbility.init(getApplicationContext())
2. Enable the HDR feature on the device. Then, the maximum brightness of the device screen will increase.
Code:
HdrAbility.setHdrAbility(true);
3. Configure the alternative maximum brightness of white points in the output video image data.
Code:
HdrAbility.setBrightness(600);
4. Make the video layer highlighted.
Code:
HdrAbility.setHdrLayer(surfaceView, true);
5. Configure the feature of highlighting the subtitle layer or the bullet comment layer.
Code:
HdrAbility.setCaptionsLayer(captionView, 1.5f);
Summary​Video resolution is an important influencer of user experience for mobile apps. HDR is often used to post-process video, but it is held back by a number of restrictions, which are resolved by the HDR Vivid SDK from Video Kit.
This SDK is loaded with features for image processing such as the OETF, tone mapping, and HDR2SDR, so that it can mimic what human eyes can see to deliver immersive videos that can be enhanced even further with the help of the HDR Ability SDK from the same kit. The functionality and straightforward integration process of these SDKs make them ideal for implementing the HDR feature into a mobile app.

Related

An introduction for HiAI General Text Recognition, Document Skew Correction and Table

Introducton:
HUAWEI HiAI is an open Artificial Intelligence (AI) capable platform for smart devices, which adopts a chip-device-cloud architecture, opening up the chip, app, and service capabilities for a fully intelligent ecosystem. The HiAI is used for Text Recognition, Image Recognition etc. Text Recognition is used to extract text data from an image and the Image recognition is used to obtain quality, category and scene of a particular image.
This article is giving a brief explanation on Document Skew Correction, General Text Recognition and Table Recognition. Here we are using DevEco plugin to configure the HiAI application. To know about the integrate of application via DevEco you can refer the article "HUAWEI HiAI Image Super-Resolution Via DevEco".
General Text Recognition:
The API detects and identifies text information in a given image. It supports text extraction from screenshots with simple to more complex backgrounds. In snapshot Optical Character Recognition(OCR), the image resolution should be higher than 720p (1280×720 px), and the aspect ratio (length-to-width ratio) should be lower than 2:1. If the input image does not meet the above requirements, text recognition accuracy may be affected.
Code Implementation:
Initialize with the VisionBase static class and asynchronously get the connection of the service.
Code:
private void initHiAI() {
VisionBase.init(this, new ConnectionCallback() {
@Override
public void onServiceConnect() {
/** Invoke the callback method when the service is connected successfully. You can also perform initialization and mark the service connection status in this method*/
}
@Override
public void onServiceDisconnect() {
/** Invoke the callback method when the service is disconnected. You can choose to reconnect to the service or handle exceptions API invocation*/
}
});
}
Define the detector instance and frame. Select the OCR type to be called via TextConfiguration and finally process the Json result.
Code:
private void setHiAi() {
/** Define the detector instance and use the Context of this project as a parameter*/
TextDetector detector = new TextDetector(this);
/** Define a frame and initialize it*/
Frame frame = new Frame();
/** Place the bitmap to be checked in the frame*/
frame.setBitmap(bitmap);
/** Select the OCR type to be called via TextConfiguration, for example TYPE_TEXT_DETECT_SCREEN_SHOT_GENERAL is the detection screenshot*/
TextConfiguration config = new TextConfiguration();
config.setEngineType(TextDetectType.TYPE_TEXT_DETECT_SCREEN_SHOT_GENERAL);
detector.setTextConfiguration(config);
/** Check the text and process the result. Note that this line of code must be placed in the working thread rather than the primary thread*/
JSONObject jsonObject = detector.detect(frame, null);
/** Run the convertResult() command to convert the JSON character string into a Java class. You can also parse the JSON character string yourself*/
Text text = detector.convertResult(jsonObject);
System.out.println("Json:"+jsonObject);
String txtValue = text.getValue();
this.txtValue = txtValue;
handler.sendEmptyMessage(Utils.HiAI_RESULT);
}
Screenshot:
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Document Skew Correction:
The API can be used to recognize automatically the location information of the document in the image and correct the angle of the document. For example, we can duplicate paper photos and letters and correct their locations, thereby reserving memories.
Here the bitmap of the frame type must be in the ARGB8888 format with a size not greater than 20 megapixels.
Code Implementation:
As mentioned above sample, we have to Initialize with the VisionBase static class. After that define detector instance and frame. Put the picture which need to be detected into the frame. Call the detect method and process the result.
Code:
private void setHiAi() {
/** Define class detector, the context of this project is the input parameter */
DocRefine docResolution = new DocRefine(this);
/** Get Bitmap image (Note: bitmap type must be ARGB8888, please pay attention to the necessary conversion)*/
/** Define frame class, and put the picture which need to be detected into the frame */
Frame frame = new Frame();
frame.setBitmap(bitmap);
/** Call the detect method to get the information of the result*/
/** Note: This line of code must be placed in the worker thread instead of the main thread */
JSONObject jsonDoc = docResolution.docDetect(frame, null);
/** Returns a jsonObject format, you can call convertResult() method to convert the json to java class and get the result*/
DocCoordinates sc = docResolution.convertResult(jsonDoc);
ImageResult sr = docResolution.docRefine(frame, sc, null);
/** Get bitmap result*/
Bitmap bmp = sr.getBitmap();
/** Note: The result and the Bitmap in the result must be NULL, but also to determine whether the returned error code is 0 (0 means no error)*/
this.bmp = bmp;
handler.sendEmptyMessage(Utils.HiAI_RESULT);
}
Screenshot:
Table Recognition:
The API can perfectly retrieve the information of tables as well as text from cells, besides, merged cells can also be recognized. It supports recognition of tables with clear and unbroken lines, but not supportive of tables with crooked lines or cells divided by color background. Currently the API supports recognition from printed materials and snapshots of slide meetings, but it is not functioning for the screenshots or photos of excel sheets and any other table editing software.
Here, the image resolution should be higher than 720p (1280×720 px), and the aspect ratio (length-to-width ratio) should be lower than 2:1.
Code Implementation:
As mentioned above sample, we have to Initialize with the VisionBase static class. After that define TableDetector instance and frame. Call the detect method and process the result.
Code:
<p style="line-height: 2.0em;">private void setHiAi() {
startTime = System.currentTimeMillis();
/** Define the detector instance and use the Context of this project as a parameter*/
TableDetector detector = new TableDetector(this);
/** Define a frame and initialize it*/
Frame frame = new Frame();
/** Place the bitmap to be checked in the frame*/
frame.setBitmap(bitmap);
/** Check the text and process the result. Note that this line of code must be placed in the working thread rather than the primary thread*/
JSONObject jsonObject = detector.detect(frame, null);
/** Run the convertResult() command to convert the JSON character string into a Java class. You can also parse the JSON character string yourself*/
Table table = detector.convertResult(jsonObject);
System.out.println("Json:"+jsonObject);
extractFromTable(table);
this.txtValue = txtValue;
handler.sendEmptyMessage(Utils.HiAI_RESULT);
}
</p>
Screenshot:
Conclusion:
In this article, we discussed about Document Skew Correction, General Text Recognition and Table Recognition. We can use these in so many real-time scenarios, as follows:
The Document Skew Correction API can be used to easily record works in exhibition regardless of the objective factors such as the angle and the stream of people.
General Text Recognition can provide accurate recognition in various scenarios, even with interferences such as complex light and background situations.
The operational scenarios of Table Recognition are various, so that the third-party apps can generate excel sheets according to the returned results, minimizing manual workload
Reference:
HiAI Guide

How to Automatically Create a Scenic Timelapse Video

{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Have you ever watched a video of the northern lights? Mesmerizing light rays that swirl and dance through the star-encrusted sky. It's even more stunning when they are backdropped by crystal-clear waters that flow smoothly between and under ice crusts. Complementing each other, the moving sky and water compose a dynamic scene that reflects the constant rhythm of the mother nature.
Now imagine that the video is frozen into an image: It still looks beautiful, but lacks the dynamism of the video. Such a contrast between still and moving images shows how videos are sometimes better than still images when it comes to capturing majestic scenery, since the former can convey more information and thus be more engaging.
This may be the reason why we sometimes regret just taking photos instead of capturing a video when we encounter beautiful scenery or a memorable moment.
In addition to this, when we try to add a static image to a short video, we will find that the transition between the image and other segments of the video appears very awkward, since the image is the only static segment in the whole video.
If we want to turn a static image into a dynamic video by adding some motion effects to the sky and water, one way to do this is to use a professional PC program to modify the image. However, this process is often very complicated and time-consuming: It requires adjustment of the timeline, frames, and much more, which can be a daunting prospect for amateur image editors.
Luckily, there are now numerous AI-driven capabilities that can automatically create time-lapse videos for users. I chose to use the auto-timelapse capability provided by HMS Core Video Editor Kit. It can automatically detect the sky and water in an image and produce vivid dynamic effects for them, just like this:
The movement speed and angle of the sky and water are customizable.
Now let's take a look at the detailed integration procedure for this capability, to better understand how such a dynamic effect is created.
Integration Procedure​Preparations​1. Configure necessary app information. This step requires you to register a developer account, create an app, generate a signing certificate fingerprint, configure the fingerprint, and enable the required services.
2. Integrate the SDK of the kit.
3. Configure the obfuscation scripts.
4. Declare necessary permissions.
Project Configuration​1. Set the app authentication information. This can be done via an API key or an access token.
Set an API key via the setApiKey method: You only need to set the app authentication information once during app initialization.
Code:
MediaApplication.getInstance().setApiKey("your ApiKey");
Or, set an access token by using the setAccessToken method: You only need to set the app authentication information once during app initialization.
Code:
MediaApplication.getInstance().setAccessToken("your access token");
2. Set a License ID. This ID should be unique because it is used to manage the usage quotas of the service.
Code:
MediaApplication.getInstance().setLicenseId("License ID");
3. Initialize the runtime environment for the HuaweiVideoEditor object. Remember to release the HuaweiVideoEditor object when exiting the project.
Create a HuaweiVideoEditor object.
Code:
HuaweiVideoEditor editor = HuaweiVideoEditor.create(getApplicationContext());
Specify the preview area position. Such an area is used to render video images, which is implemented by SurfaceView created within the SDK. Before creating such an area, specify its position in the app first.
Code:
<LinearLayout
android:id="@+id/video_content_layout"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/video_edit_main_bg_color"
android:gravity="center"
android:orientation="vertical" />
// Specify the preview area position.
LinearLayout mSdkPreviewContainer = view.findViewById(R.id.video_content_layout);
// Specify the preview area layout.
editor.setDisplay(mSdkPreviewContainer);
Initialize the runtime environment. If license verification fails, LicenseException will be thrown.
After it is created, the HuaweiVideoEditor object will not occupy any system resources. You need to manually set when the runtime environment of the object will be initialized. Once you have done this, necessary threads and timers will be created within the SDK.
Code:
try {
editor.initEnvironment();
} catch (LicenseException error) {
SmartLog.e(TAG, "initEnvironment failed: " + error.getErrorMsg());
finish();
return;
}
Function Development​
Code:
// Initialize the auto-timelapse engine.
imageAsset.initTimeLapseEngine(new HVEAIInitialCallback() {
@Override
public void onProgress(int progress) {
// Callback when the initialization progress is received.
}
@Override
public void onSuccess() {
// Callback when the initialization is successful.
}
@Override
public void onError(int errorCode, String errorMessage) {
// Callback when the initialization failed.
}
});
// When the initialization is successful, check whether there is sky or water in the image.
int motionType = -1;
imageAsset.detectTimeLapse(new HVETimeLapseDetectCallback() {
@Override
public void onResult(int state) {
// Record the state parameter, which is used to define a motion effect.
motionType = state;
}
});
// skySpeed indicates the speed at which the sky moves; skyAngle indicates the direction to which the sky moves; waterSpeed indicates the speed at which the water moves; waterAngle indicates the direction to which the water moves.
HVETimeLapseEffectOptions options =
new HVETimeLapseEffectOptions.Builder().setMotionType(motionType)
.setSkySpeed(skySpeed)
.setSkyAngle(skyAngle)
.setWaterAngle(waterAngle)
.setWaterSpeed(waterSpeed)
.build();
// Add the auto-timelapse effect.
imageAsset.addTimeLapseEffect(options, new HVEAIProcessCallback() {
@Override
public void onProgress(int progress) {
}
@Override
public void onSuccess() {
}
@Override
public void onError(int errorCode, String errorMessage) {
}
});
// Stop applying the auto-timelapse effect.
imageAsset.interruptTimeLapse();
// Remove the auto-timelapse effect.
imageAsset.removeTimeLapseEffect();
Now, the auto-timelapse capability has been successfully integrated into an app.
Conclusion​When capturing scenic vistas, videos, which can show the dynamic nature of the world around us, are often a better choice than static images. In addition, when creating videos with multiple shots, dynamic pictures deliver a smoother transition effect than static ones.
However, for users not familiar with the process of animating static images, if they try do so manually using computer software, they may find the results unsatisfying.
The good news is that there are now mobile apps integrated with capabilities such as Video Editor Kit's auto-timelapse feature that can create time-lapse effects for users. The generated effect appears authentic and natural, the capability is easy to use, and its integration is straightforward. With such capabilities in place, a video/image app can provide users with a more captivating user experience.
In addition to video/image editing apps, I believe the auto-timelapse capability can also be utilized by many other types of apps. What other kinds of apps do you think would benefit from such a feature? Let me know in the comments section.

How I Created a Smart Video Clip Extractor

{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Travel and life vlogs are popular among app users: Those videos are telling, covering all the most attractive parts in a journey or a day. To create such a video first requires great editing efforts to cut out the trivial and meaningless segments in the original video, which used to be a thing of video editing pros.
This is no longer the case. Now we have an array of intelligent mobile apps that can help us automatically extract highlights from a video, so we can focus more on spicing up the video by adding special effects, for example. I opted to use the highlight capability from HMS Core Video Editor Kit to create my own vlog editor.
How It Works​This capability assesses how appealing video frames are and then extracts the most suitable ones. To this end, it is said that the capability takes into consideration the video properties most concerned by users, a conclusion that is drawn from survey and experience assessment from users. On the basis of this, the highlight capability develops a comprehensive frame assessment scheme that covers various aspects. For example:
Aesthetics evaluation. This aspect is a data set built upon composition, lighting, color, and more, which is the essential part of the capability.
Tags and facial expressions. They represent the frames that are detected and likely to be extracted by the highlight capability, such as frames that contain people, animals, and laughter.
Frame quality and camera movement mode. The capability discards low-quality frames that are blurry, out-of-focus, overexposed, or shaky, to ensure such frames will not impact the quality of the finished video. Amazingly, despite all of these, the highlight capability is able to complete the extraction process in just 2 seconds.
See for yourself how the finished video by the highlight capability compares with the original video.
Backing Technology​The highlight capability stands out from the crowd by adopting models and a frame assessment scheme that are iteratively optimized. Technically and specifically speaking:
The capability introduces AMediaCodec for hardware decoding and Open Graphics Library (OpenGL) for rendering frames and automatically adjusting the frame dimensions according to the screen dimensions. The capability algorithm uses multiple neural network models. In this way, the capability checks the device model where it runs and then automatically chooses to run on NPU, CPU, or GPU. Consequently, the capability delivers a higher running performance.
To provide the extraction result more quickly, the highlight capability uses the two-stage algorithm of sparse sampling to dense sampling, checks how content distributed among numerous videos, and adopts the frame buffer. All these contribute to a higher efficiency of determining the most attractive video frames. To ensure high performance of the algorithm, the capability adopts the thread pool scheduling and producer-consumer model, to ensure that the video decoder and models can run at the same time.
During the sparse sampling stage, the capability decodes and processes some (up to 15) key frames in a video. The interval between the key frames is no less than 2 seconds. During the dense sampling stage, the algorithm picks out the best key frame and then extracts frames before and after to further analyze the highlighted part of the video.
The extraction result is closely related to the key frame position. The processing result of the highlight capability will not be ideal when the sampling points are not dense enough because, for example, the video does not have enough key frames or the duration is too long (greater than 1 minute). For the capability to deliver optimal performance, it recommends that the duration of the input video be less than 60 seconds.
Let's now move on to how this capability can be integrated.
Integration Process​Preparations​Make necessary preparations before moving on to the next part. Required steps include:
Configure the app information in AppGallery Connect.
Integrate the SDK of HMS Core.
Configure obfuscation scripts.
Declare necessary permissions.
Setting up the Video Editing Project​1. Configure the app authentication information by using either an access token or API key.
Method 1: Call setAccessToken to set an access token, which is required only once during app startup.
Code:
MediaApplication.getInstance().setAccessToken("your access token");
Method 2: Call setApiKey to set an API key, which is required only once during app startup.
Code:
MediaApplication.getInstance().setApiKey("your ApiKey");
2. Set a License ID.
This ID is used to manage the usage quotas of Video Editor Kit and must be unique.
Code:
MediaApplication.getInstance().setLicenseId("License ID");
Initialize the runtime environment of HuaweiVideoEditor.
When creating a video editing project, we first need to create an instance of HuaweiVideoEditor and initialize its runtime environment. When you exit the project, the instance shall be released.
Create an instance of HuaweiVideoEditor.
Code:
HuaweiVideoEditor editor = HuaweiVideoEditor.create(getApplicationContext());
Determine the layout of the preview area.
Such an area renders video images, and this is implemented by SurfaceView within the fundamental capability SDK. Before the area is created, we need to specify its layout.
Code:
<LinearLayout
android:id="@+id/video_content_layout"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/video_edit_main_bg_color"
android:gravity="center"
android:orientation="vertical" />
// Specify a preview area.
LinearLayout mSdkPreviewContainer = view.findViewById(R.id.video_content_layout);
// Design the layout of the area.
editor.setDisplay(mSdkPreviewContainer);
Initialize the runtime environment. If the license verification fails, LicenseException will be thrown.
After the HuaweiVideoEditor instance is created, it will not use any system resources, and we need to manually set the initialization time for the runtime environment. Then, the fundamental capability SDK will internally create necessary threads and timers.
Code:
try {
editor.initEnvironment();
} catch (LicenseException error) {
SmartLog.e(TAG, "initEnvironment failed: " + error.getErrorMsg());
finish();
return;
}
Integrating the Highlight Capability​
Code:
// Create an object that will be processed by the highlight capability.
HVEVideoSelection hveVideoSelection = new HVEVideoSelection();
// Initialize the engine of the highlight capability.
hveVideoSelection.initVideoSelectionEngine(new HVEAIInitialCallback() {
@Override
public void onProgress(int progress) {
// Callback when the initialization progress is received.
}
@Override
public void onSuccess() {
// Callback when the initialization is successful.
}
@Override
public void onError(int errorCode, String errorMessage) {
// Callback when the initialization failed.
}
});
// After the initialization is successful, extract the highlighted video. filePath indicates the video file path, and duration indicates the desired duration for the highlighted video.
hveVideoSelection.getHighLight(filePath, duration, new HVEVideoSelectionCallback() {
@Override
public void onResult(long start) {
// The highlighted video is successfully extracted.
}
});
// Release the highlight engine.
hveVideoSelection.releaseVideoSelectionEngine();
Conclusion​The vlog has been playing a vital part in this we-media era since its appearance. In the past, there were just a handful of people who could create a vlog, because the process of picking out the most interesting part from the original video could be so demanding.
Thanks to smart mobile app technology, even video editing amateurs can now create a vlog because much of the process can be completed automatically by an app with the function of highlighted video extraction.
The highlight capability from the Video Editor Kit is one such function. This capability introduces a set of features to deliver incredible results, such as AMediaCodec, OpenGL, neural networks, a two-stage algorithm (sparse sampling to dense sampling), and more. This capability can help create either a highlighted video extractor or build a highlighted video extraction feature in an app.

Streamlining 3D Animation Creation via Rigging

{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
I dare say there are two types of people in this world: people who love Toy Story and people who have not watched it.
Well, this is just the opinion of a huge fan of the animation film. When I was a child, I always dreamed of having toys that could move and play with me, like my own Buzz Lightyear. Thanks to a fancy technique called rigging, I can now bring my toys to life, albeit I'm probably too old for them now.
What Is Rigging in 3D Animation and Why Do We Need It?​Put simply, rigging is a process whereby a skeleton is created for a 3D model to make it move. In other words, rigging creates a set of connected virtual bones that are used to control a 3D model.
It paves the way for animation because it enables a model to be deformed, making it moveable, which is the very reason that rigging is necessary for 3D animation.
What Is Auto Rigging​3D animation has been adopted by mobile apps in a number of fields (gaming, e-commerce, video, and more), to achieve more realistic animations than 2D.
However, this graphic technique has daunted many developers (like me) because rigging, one of its major prerequisites, is difficult and time-consuming for people who are unfamiliar with modeling. Specifically speaking, most high-performing rigging solutions have many requirements. An example of this is that the input model should be in a standard position, seven or eight key skeletal points should be added, as well as inverse kinematics which must be added to the bones, and more.
Luckily, there are solutions that can automatically complete rigging, such as the auto rigging solution from HMS Core 3D Modeling Kit.
This capability delivers a wholly automated rigging process, requiring just a biped humanoid model that is generated using images taken from a mobile phone camera. After the model is input, auto rigging uses AI algorithms for limb rigging and generates the model skeleton and skin weights (which determine the degree to which a bone can influence a part of the mesh). Then, the capability changes the orientation and position of the skeleton so that the model can perform a range of preset actions, like walking, running, and jumping. Besides, the rigged model can also be moved according to an action generated by using motion capture technology, or be imported into major 3D engines for animation.
Lower requirements do not compromise rigging accuracy. Auto rigging is built upon hundreds of thousands of 3D model rigging data records. Thanks to some fine-tuned data records, the capability delivers ideal algorithm accuracy and generalization.
I know that words alone are no proof, so check out the animated model I've created using the capability.
Movement is smooth, making the cute panda move almost like a real one. Now I'd like to show you how I created this model and how I integrated auto rigging into my demo app.
Integration Procedure​Preparations​Before moving on to the real integration work, make necessary preparations, which include:
Configure app information in AppGallery Connect.
Integrate the HMS Core SDK with the app project, which includes Maven repository address configuration.
Configure obfuscation scripts.
Declare necessary permissions.
Capability Integration​1. Set an access token or API key — which can be found in agconnect-services.json — during app initialization for app authentication.
Using the access token: Call setAccessToken to set an access token. This task is required only once during app initialization.
Code:
ReconstructApplication.getInstance().setAccessToken("your AccessToken");
Using the API key: Call setApiKey to set an API key. This key does not need to be set again.
Code:
ReconstructApplication.getInstance().setApiKey("your api_key");
The access token is recommended. And if you prefer the API key, it is assigned to the app when it is created in AppGallery Connect.
2. Create a 3D object reconstruction engine and initialize it. Then, create an auto rigging configurator.
Code:
// Create a 3D object reconstruction engine.
Modeling3dReconstructEngine modeling3dReconstructEngine = Modeling3dReconstructEngine.getInstance(context);
// Create an auto rigging configurator.
Modeling3dReconstructSetting setting = new Modeling3dReconstructSetting.Factory()
// Set the working mode of the engine to PICTURE.
.setReconstructMode(Modeling3dReconstructConstants.ReconstructMode.PICTURE)
// Set the task type to auto rigging.
.setTaskType(Modeling3dReconstructConstants.TaskType.AUTO_RIGGING)
.create();
3. Create a listener for the result of uploading images of an object.
Code:
private Modeling3dReconstructUploadListener uploadListener = new Modeling3dReconstructUploadListener() {
@Override
public void onUploadProgress(String taskId, double progress, Object ext) {
// Callback when the upload progress is received.
}
@Override
public void onResult(String taskId, Modeling3dReconstructUploadResult result, Object ext) {
// Callback when the upload is successful.
}
@Override
public void onError(String taskId, int errorCode, String message) {
// Callback when the upload failed.
}
};
4. Use a 3D object reconstruction configurator to initialize the task, set an upload listener for the engine created in step 1, and upload images.
Code:
// Use the configurator to initialize the task, which should be done in a sub-thread.
Modeling3dReconstructInitResult modeling3dReconstructInitResult = modeling3dReconstructEngine.initTask(setting);
String taskId = modeling3dReconstructInitResult.getTaskId();
// Set an upload listener.
modeling3dReconstructEngine.setReconstructUploadListener(uploadListener);
// Call the uploadFile API of the 3D object reconstruction engine to upload images.
modeling3dReconstructEngine.uploadFile(taskId, filePath);
5. Query the status of the auto rigging task.
Code:
// Initialize the task processing class.
Modeling3dReconstructTaskUtils modeling3dReconstructTaskUtils = Modeling3dReconstructTaskUtils.getInstance(context);
// Call queryTask in a sub-thread to query the task status.
Modeling3dReconstructQueryResult queryResult = modeling3dReconstructTaskUtils.queryTask(taskId);
// Obtain the task status.
int status = queryResult.getStatus();
6. Create a listener for the result of model file download.
Code:
private Modeling3dReconstructDownloadListener modeling3dReconstructDownloadListener = new Modeling3dReconstructDownloadListener() {
@Override
public void onDownloadProgress(String taskId, double progress, Object ext) {
// Callback when download progress is received.
}
@Override
public void onResult(String taskId, Modeling3dReconstructDownloadResult result, Object ext) {
// Callback when download is successful.
}
@Override
public void onError(String taskId, int errorCode, String message) {
// Callback when download failed.
}
};
7. Pass the download listener to the 3D object reconstruction engine, to download the rigged model.
Code:
// Set download configurations.
Modeling3dReconstructDownloadConfig downloadConfig = new Modeling3dReconstructDownloadConfig.Factory()
// Set the model file format to OBJ or glTF.
.setModelFormat(Modeling3dReconstructConstants.ModelFormat.OBJ)
// Set the texture map mode to normal mode or PBR mode.
.setTextureMode(Modeling3dReconstructConstants.TextureMode.PBR)
.create();
// Set the download listener.
modeling3dReconstructEngine.setReconstructDownloadListener(modeling3dReconstructDownloadListener);
// Call downloadModelWithConfig, passing the task ID, path to which the downloaded file will be saved, and download configurations, to download the rigged model.
modeling3dReconstructEngine.downloadModelWithConfig(taskId, fileSavePath, downloadConfig);
Where to Use​Auto rigging is used in many scenarios, for example:
Gaming. The most direct way of using auto rigging is to create moveable characters in a 3D game. Or, I think we can combine it with AR to create animated models that can appear in the camera display of a mobile device, which will interact with users.
Online education. We can use auto rigging to animate 3D models of popular toys, and liven them up with dance moves, voice-overs, and nursery rhymes to create educational videos. These models can be used in educational videos to appeal to kids more.
E-commerce. Anime figurines look rather plain compared to how they behave in animes. To spice up the figurines, we can use auto rigging to animate 3D models that will look more engaging and dynamic.
Conclusion​3D animation is widely used in mobile apps, because it presents objects in a more fun and interactive way.
A key technique for creating great 3D animations is rigging. Conventional rigging requires modeling know-how and expertise, which puts off many amateur modelers.
Auto rigging is the perfect solution to this challenge because its full-automated rigging process can produce highly accurate rigged models that can be easily animated using major engines available on the market. Auto rigging not only lowers the costs and requirements of 3D model generation and animation, but also helps 3D models look more appealing.

Create Realistic Lighting with DDGI

Why We Need DDGI​Of all the things that make a 3D game immersive, global illumination effects (including reflections, refractions, and shadows) are undoubtedly the jewel in the crown. Simply put, bad lighting can ruin an otherwise great game experience.
A technique for creating real-life lighting is known as dynamic diffuse global illumination (DDGI for short). This technique delivers real-time rendering for games, decorating game scenes with delicate and appealing visuals. In other words, DDGI brings out every color in a scene by dynamically changing the lighting, realizing the distinct relationship between objects and scene temperature, as well as enriching levels of representation for information in a scene.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Scene rendered with direct lighting vs. scene rendered with DDGI​
Implementing a scene with lighting effects like those in the image on the right requires significant technical power — And this is not the only challenge. Different materials react in different ways to light. Such differences are represented via diffuse reflection that equally scatters lighting information including illuminance, light movement direction, and light movement speed. Skillfully handling all these variables requires a high-performing development platform with massive computing power.
Luckily, the DDGI plugin from HMS Core Scene Kit is an ideal solution to all these challenges, which supports mobile apps, and can be extended to all operating systems, with no need for pre-baking. Utilizing the light probe, the plugin adopts an improved algorithm when updating and coloring probes. In this way, the computing loads of the plugin are lower than those of a traditional DDGI solution. The plugin simulates multiple reflections of light against object surfaces, to bolster a mobile app with dynamic, interactive, and realistic lighting effects.
The fabulous lighting effects found in the scene are created using the plugin just mentioned, which — and I'm not lying — takes merely several simple steps to do. Then let's dive into the steps to know how to equip an app with this plugin.
Development Procedure​Overview​1. Initialization phase: Configure a Vulkan environment and initialize the DDGIAPI class.
2. Preparation phase:
Create two textures that will store the rendering results of the DDGI plugin, and pass the texture information to the plugin.
Prepare the information needed and then pass it on to the plugin. Such information includes data of the mesh, material, light source, camera, and resolution.
Set necessary parameters for the plugin.
3. Rendering phase:
When the information about the transformation matrix applied to a mesh, light source, or camera changes, the new information will be passed to the DDGI plugin.
Call the Render() function to perform rendering and save the rendering results of the DDGI plugin to the textures created in the preparation phase.
Apply the rendering results of the DDGI plugin to shading calculations.
Art Restrictions​1. When using the DDGI plugin for a scene, set origin in step 6 in the Procedure part below to the center coordinates of the scene, and configure the count of probes and ray marching accordingly. This helps ensure that the volume of the plugin can cover the whole scene.
2. To enable the DDGI plugin to simulate light obstruction in a scene, ensure walls in the scene all have a proper level of thickness (which should be greater than the probe density). Otherwise, the light leaking issue will arise. On top of this, I recommend that you create a wall consisting of two single-sided planes.
3. The DDGI plugin is specifically designed for mobile apps. Taking performance and power consumption into consideration, it is recommended (not required) that:
The vertex count of meshes passed to the DDGI plugin be less than or equal to 50,000, so as to control the count of meshes. For example, pass only the main structures that will create indirect light.
The density and count of probes be up to 10 x 10 x 10.
Procedure​1. Download the package of the DDGI plugin and decompress the package. One header file and two SO files for Android will be obtained. You can find the package here.
2. Use CMake to create a CMakeLists.txt file. The following is an example of the file.
Code:
cmake_minimum_required(VERSION 3.4.1 FATAL_ERROR)
set(NAME DDGIExample)
project(${NAME})
set(PROJ_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -O2 -DNDEBUG -DVK_USE_PLATFORM_ANDROID_KHR")
file(GLOB EXAMPLE_SRC "${PROJ_ROOT}/src/*.cpp") # Write the code for calling the DDGI plugin by yourself.
include_directories(${PROJ_ROOT}/include) # Import the header file. That is, put the DDGIAPI.h header file in this directory.
# Import two SO files (librtcore.so and libddgi.so).
ADD_LIBRARY(rtcore SHARED IMPORTED)
SET_TARGET_PROPERTIES(rtcore
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/libs/librtcore.so)
ADD_LIBRARY(ddgi SHARED IMPORTED)
SET_TARGET_PROPERTIES(ddgi
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/libs/libddgi.so)
add_library(native-lib SHARED ${EXAMPLE_SRC})
target_link_libraries(
native-lib
...
ddgi # Link the two SO files to the app.
rtcore
android
log
z
...
)
3. Configure a Vulkan environment and initialize the DDGIAPI class.
Code:
// Set the Vulkan environment information required by the DDGI plugin,
// including logicalDevice, queue, and queueFamilyIndex.
void DDGIExample::SetupDDGIDeviceInfo()
{
m_ddgiDeviceInfo.physicalDevice = physicalDevice;
m_ddgiDeviceInfo.logicalDevice = device;
m_ddgiDeviceInfo.queue = queue;
m_ddgiDeviceInfo.queueFamilyIndex = vulkanDevice->queueFamilyIndices.graphics;
}
void DDGIExample::PrepareDDGI()
{
// Set the Vulkan environment information.
SetupDDGIDeviceInfo();
// Call the initialization function of the DDGI plugin.
m_ddgiRender->InitDDGI(m_ddgiDeviceInfo);
...
}
void DDGIExample::Prepare()
{
...
// Create a DDGIAPI object.
std::unique_ptr<DDGIAPI> m_ddgiRender = make_unique<DDGIAPI>();
...
PrepareDDGI();
...
}
4. Create two textures: one for storing the irradiance results (that is, diffuse global illumination from the camera view) and the other for storing the normal and depth. To improve the rendering performance, you can set a lower resolution for the two textures. A lower resolution brings a better rendering performance, but also causes distorted rendering results such as sawtooth edges.
Code:
// Create two textures for storing the rendering results.
void DDGIExample::CreateDDGITexture()
{
VkImageUsageFlags usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
int ddgiTexWidth = width / m_shadingPara.ddgiDownSizeScale; // Texture width.
int ddgiTexHeight = height / m_shadingPara.ddgiDownSizeScale; // Texture height.
glm::ivec2 size(ddgiTexWidth, ddgiTexHeight);
// Create a texture for storing the irradiance results.
m_irradianceTex.CreateAttachment(vulkanDevice,
ddgiTexWidth,
ddgiTexHeight,
VK_FORMAT_R16G16B16A16_SFLOAT,
usage,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
m_defaultSampler);
// Create a texture for storing the normal and depth.
m_normalDepthTex.CreateAttachment(vulkanDevice,
ddgiTexWidth,
ddgiTexHeight,
VK_FORMAT_R16G16B16A16_SFLOAT,
usage,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
m_defaultSampler);
}
// Set the DDGIVulkanImage information.
void DDGIExample::PrepareDDGIOutputTex(const vks::Texture& tex, DDGIVulkanImage *texture) const
{
texture->image = tex.image;
texture->format = tex.format;
texture->type = VK_IMAGE_TYPE_2D;
texture->extent.width = tex.width;
texture->extent.height = tex.height;
texture->extent.depth = 1;
texture->usage = tex.usage;
texture->layout = tex.imageLayout;
texture->layers = 1;
texture->mipCount = 1;
texture->samples = VK_SAMPLE_COUNT_1_BIT;
texture->tiling = VK_IMAGE_TILING_OPTIMAL;
}
void DDGIExample::PrepareDDGI()
{
...
// Set the texture resolution.
m_ddgiRender->SetResolution(width / m_downScale, height / m_downScale);
// Set the DDGIVulkanImage information, which tells your app how and where to store the rendering results.
PrepareDDGIOutputTex(m_irradianceTex, &m_ddgiIrradianceTex);
PrepareDDGIOutputTex(m_normalDepthTex, &m_ddgiNormalDepthTex);
m_ddgiRender->SetAdditionalTexHandler(m_ddgiIrradianceTex, AttachmentTextureType::DDGI_IRRADIANCE);
m_ddgiRender->SetAdditionalTexHandler(m_ddgiNormalDepthTex, AttachmentTextureType::DDGI_NORMAL_DEPTH);
...
}
void DDGIExample::Prepare()
{
...
CreateDDGITexture();
...
PrepareDDGI();
...
}
Code:
// Create two textures for storing the rendering results.
void DDGIExample::CreateDDGITexture()
{
VkImageUsageFlags usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
int ddgiTexWidth = width / m_shadingPara.ddgiDownSizeScale; // Texture width.
int ddgiTexHeight = height / m_shadingPara.ddgiDownSizeScale; // Texture height.
glm::ivec2 size(ddgiTexWidth, ddgiTexHeight);
// Create a texture for storing the irradiance results.
m_irradianceTex.CreateAttachment(vulkanDevice,
ddgiTexWidth,
ddgiTexHeight,
VK_FORMAT_R16G16B16A16_SFLOAT,
usage,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
m_defaultSampler);
// Create a texture for storing the normal and depth.
m_normalDepthTex.CreateAttachment(vulkanDevice,
ddgiTexWidth,
ddgiTexHeight,
VK_FORMAT_R16G16B16A16_SFLOAT,
usage,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
m_defaultSampler);
}
// Set the DDGIVulkanImage information.
void DDGIExample::PrepareDDGIOutputTex(const vks::Texture& tex, DDGIVulkanImage *texture) const
{
texture->image = tex.image;
texture->format = tex.format;
texture->type = VK_IMAGE_TYPE_2D;
texture->extent.width = tex.width;
texture->extent.height = tex.height;
texture->extent.depth = 1;
texture->usage = tex.usage;
texture->layout = tex.imageLayout;
texture->layers = 1;
texture->mipCount = 1;
texture->samples = VK_SAMPLE_COUNT_1_BIT;
texture->tiling = VK_IMAGE_TILING_OPTIMAL;
}
void DDGIExample::PrepareDDGI()
{
...
// Set the texture resolution.
m_ddgiRender->SetResolution(width / m_downScale, height / m_downScale);
// Set the DDGIVulkanImage information, which tells your app how and where to store the rendering results.
PrepareDDGIOutputTex(m_irradianceTex, &m_ddgiIrradianceTex);
PrepareDDGIOutputTex(m_normalDepthTex, &m_ddgiNormalDepthTex);
m_ddgiRender->SetAdditionalTexHandler(m_ddgiIrradianceTex, AttachmentTextureType::DDGI_IRRADIANCE);
m_ddgiRender->SetAdditionalTexHandler(m_ddgiNormalDepthTex, AttachmentTextureType::DDGI_NORMAL_DEPTH);
...
}
void DDGIExample::Prepare()
{
...
CreateDDGITexture();
...
PrepareDDGI();
...
}
5. Prepare the mesh, material, light source, and camera information required by the DDGI plugin to perform rendering.
Code:
// Mesh structure, which supports submeshes.
struct DDGIMesh {
std::string meshName;
std::vector<DDGIVertex> meshVertex;
std::vector<uint32_t> meshIndice;
std::vector<DDGIMaterial> materials;
std::vector<uint32_t> subMeshStartIndexes;
...
};
// Directional light structure. Currently, only one directional light is supported.
struct DDGIDirectionalLight {
CoordSystem coordSystem = CoordSystem::RIGHT_HANDED;
int lightId;
DDGI::Mat4f localToWorld;
DDGI::Vec4f color;
DDGI::Vec4f dirAndIntensity;
};
// Main camera structure.
struct DDGICamera {
DDGI::Vec4f pos;
DDGI::Vec4f rotation;
DDGI::Mat4f viewMat;
DDGI::Mat4f perspectiveMat;
};
// Set the light source information for the DDGI plugin.
void DDGIExample::SetupDDGILights()
{
m_ddgiDirLight.color = VecInterface(m_dirLight.color);
m_ddgiDirLight.dirAndIntensity = VecInterface(m_dirLight.dirAndPower);
m_ddgiDirLight.localToWorld = MatInterface(inverse(m_dirLight.worldToLocal));
m_ddgiDirLight.lightId = 0;
}
// Set the camera information for the DDGI plugin.
void DDGIExample::SetupDDGICamera()
{
m_ddgiCamera.pos = VecInterface(m_camera.viewPos);
m_ddgiCamera.rotation = VecInterface(m_camera.rotation, 1.0);
m_ddgiCamera.viewMat = MatInterface(m_camera.matrices.view);
glm::mat4 yFlip = glm::mat4(1.0f);
yFlip[1][1] = -1;
m_ddgiCamera.perspectiveMat = MatInterface(m_camera.matrices.perspective * yFlip);
}
// Prepare the mesh information required by the DDGI plugin.
// The following is an example of a scene in glTF format.
void DDGIExample::PrepareDDGIMeshes()
{
for (constauto& node : m_models.scene.linearNodes) {
DDGIMesh tmpMesh;
tmpMesh.meshName = node->name;
if (node->mesh) {
tmpMesh.meshName = node->mesh->name; // Mesh name.
tmpMesh.localToWorld = MatInterface(node->getMatrix()); // Transformation matrix of the mesh.
// Skeletal skinning matrix of the mesh.
if (node->skin) {
tmpMesh.hasAnimation = true;
for (auto& matrix : node->skin->inverseBindMatrices) {
tmpMesh.boneTransforms.emplace_back(MatInterface(matrix));
}
}
// Material node information and vertex buffer of the mesh.
for (vkglTF::Primitive *primitive : node->mesh->primitives) {
...
}
}
m_ddgiMeshes.emplace(std::make_pair(node->index, tmpMesh));
}
}
void DDGIExample::PrepareDDGI()
{
...
// Convert these settings into the format required by the DDGI plugin.
SetupDDGILights();
SetupDDGICamera();
PrepareDDGIMeshes();
...
// Pass the settings to the DDGI plugin.
m_ddgiRender->SetMeshs(m_ddgiMeshes);
m_ddgiRender->UpdateDirectionalLight(m_ddgiDirLight);
m_ddgiRender->UpdateCamera(m_ddgiCamera);
...
}
6. Set parameters such as the position and quantity of DDGI probes.
Code:
// Set the DDGI algorithm parameters.
void DDGIExample::SetupDDGIParameters()
{
m_ddgiSettings.origin = VecInterface(3.5f, 1.5f, 4.25f, 0.f);
m_ddgiSettings.probeStep = VecInterface(1.3f, 0.55f, 1.5f, 0.f);
...
}
void DDGIExample::PrepareDDGI()
{
...
SetupDDGIParameters();
...
// Pass the settings to the DDGI plugin.
m_ddgiRender->UpdateDDGIProbes(m_ddgiSettings);
...
}
7. Call the Prepare() function of the DDGI plugin to parse the received data.
Code:
void DDGIExample::PrepareDDGI()
{
...
m_ddgiRender->Prepare();
}
8. Call the Render() function of the DDGI plugin to cache the diffuse global illumination updates to the textures created in step 4.
Notes:
In this version, the rendering results are two textures: one for storing the irradiance results and the other for storing the normal and depth. Then, you can use the bilateral filter algorithm and the texture that stores the normal and depth to perform upsampling for the texture that stores the irradiance results and obtain the final diffuse global illumination results through certain calculations.
If the Render() function is not called, the rendering results are for the scene before the changes happen.
Code:
#define RENDER_EVERY_NUM_FRAME 2
void DDGIExample::Draw()
{
...
// Call DDGIRender() once every two frames.
if (m_ddgiON && m_frameCnt % RENDER_EVERY_NUM_FRAME == 0) {
m_ddgiRender->UpdateDirectionalLight(m_ddgiDirLight); // Update the light source information.
m_ddgiRender->UpdateCamera(m_ddgiCamera); // Update the camera information.
m_ddgiRender->DDGIRender(); // Use the DDGI plugin to perform rendering once and save the rendering results to the textures created in step 4.
}
...
}
void DDGIExample::Render()
{
if (!prepared) {
return;
}
SetupDDGICamera();
if (!paused || m_camera.updated) {
UpdateUniformBuffers();
}
Draw();
m_frameCnt++;
}
9. Apply the global illumination (also called indirect illumination) effects of the DDGI plugin as follows.
Code:
// Apply the rendering results of the DDGI plugin to shading calculations.
// Perform upsampling to calculate the DDGI results based on the screen space coordinates.
vec3 Bilateral(ivec2 uv, vec3 normal)
{
...
}
void main()
{
...
vec3 result = vec3(0.0);
result += DirectLighting();
result += IndirectLighting();
vec3 DDGIIrradiances = vec3(0.0);
ivec2 texUV = ivec2(gl_FragCoord.xy);
texUV.y = shadingPara.ddgiTexHeight - texUV.y;
if (shadingPara.ddgiDownSizeScale == 1) { // Use the original resolution.
DDGIIrradiances = texelFetch(irradianceTex, texUV, 0).xyz;
} else { // Use a lower resolution.
ivec2 inDirectUV = ivec2(vec2(texUV) / vec2(shadingPara.ddgiDownSizeScale));
DDGIIrradiances = Bilateral(inDirectUV, N);
}
result += DDGILighting();
...
Image = vec4(result_t, 1.0);
}
Now the DDGI plugin is integrated, and the app can unleash dynamic lighting effects.
Takeaway​DDGI is a technology widely adopted in 3D games to make games feel more immersive and real, by delivering dynamic lighting effects. However, traditional DDGI solutions are demanding, and it is challenging to integrate one into a mobile app.
Scene Kit breaks down these barriers, by introducing its DDGI plugin. The high performance and easy integration of this DDGI plugin is ideal for developers who want to create realistic lighting in apps.

Categories

Resources